@blocklet/payment-react
Version:
Reusable react components for payment kit v2
834 lines (833 loc) • 30.7 kB
JavaScript
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import "react-international-phone/style.css";
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
import Toast from "@arcblock/ux/lib/Toast";
import { Box, Button, CircularProgress, Divider, Fade, Stack, Tooltip, Typography } from "@mui/material";
import { useMemoizedFn, useSetState } from "ahooks";
import pWaitFor from "p-wait-for";
import { useEffect, useMemo, useRef } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { joinURL } from "ufo";
import { dispatch } from "use-bus";
import isEmail from "validator/es/lib/isEmail";
import { fromUnitToToken } from "@ocap/util";
import DID from "@arcblock/ux/lib/DID";
import isEmpty from "lodash/isEmpty";
import { HelpOutline, OpenInNew } from "@mui/icons-material";
import FormInput from "../../components/input.js";
import FormLabel from "../../components/label.js";
import { usePaymentContext } from "../../contexts/payment.js";
import { useSubscription } from "../../hooks/subscription.js";
import api from "../../libs/api.js";
import {
flattenPaymentMethods,
formatError,
formatQuantityInventory,
getPrefix,
getStatementDescriptor,
getTokenBalanceLink,
isCrossOrigin
} from "../../libs/util.js";
import AddressForm from "./address.js";
import CurrencySelector from "./currency.js";
import PhoneInput from "./phone.js";
import StripeCheckout from "./stripe/index.js";
import { useMobile } from "../../hooks/mobile.js";
import { formatPhone, validatePhoneNumber } from "../../libs/phone-validator.js";
import LoadingButton from "../../components/loading-button.js";
import OverdueInvoicePayment from "../../components/over-due-invoice-payment.js";
import { saveCurrencyPreference } from "../../libs/currency.js";
import ConfirmDialog from "../../components/confirm.js";
import { getFieldValidation } from "../../libs/validator.js";
export const waitForCheckoutComplete = async (sessionId) => {
let result;
await pWaitFor(
async () => {
const { data } = await api.get(`/api/checkout-sessions/retrieve/${sessionId}`);
if (data.paymentIntent && data.paymentIntent.status === "requires_action" && data.paymentIntent.last_payment_error) {
throw new Error(data.paymentIntent.last_payment_error.message);
}
result = data;
return (
// eslint-disable-next-line @typescript-eslint/return-await
data.checkoutSession?.status === "complete" && ["paid", "no_payment_required"].includes(data.checkoutSession?.payment_status)
);
},
{ interval: 2e3, timeout: 3 * 60 * 1e3 }
);
return result;
};
export const hasDidWallet = (user) => {
const connected = user?.connectedAccounts || user?.extraConfigs?.connectedAccounts || [];
return connected.some((x) => x.provider === "wallet");
};
const setUserFormValues = (userInfo, currentValues, setValue, options = {}) => {
const { preferExisting = true, shouldValidate = false } = options;
const basicFields = {
customer_name: userInfo.name || userInfo.fullName,
customer_email: userInfo.email,
customer_phone: formatPhone(userInfo.phone)
};
const addressFields = {
"billing_address.state": userInfo.address?.state || userInfo.address?.province,
"billing_address.line1": userInfo.address?.line1,
"billing_address.line2": userInfo.address?.line2,
"billing_address.city": userInfo.address?.city,
"billing_address.postal_code": userInfo.address?.postal_code || userInfo.address?.postalCode,
"billing_address.country": userInfo.address?.country || "us"
};
if (options.showPhone) {
addressFields["billing_address.country"] = userInfo.metadata?.phone?.country || userInfo.address?.country;
}
const allFields = { ...addressFields, ...basicFields };
Object.entries(allFields).forEach(([field, value]) => {
if (!preferExisting || !currentValues[field.split(".")[0]]) {
setValue(field, value, { shouldValidate });
}
});
};
export default function PaymentForm({
checkoutSession,
paymentMethods,
paymentIntent,
paymentLink,
customer,
onPaid,
onError,
// mode,
action,
onlyShowBtn = false,
isDonation = false
}) {
const { t, locale } = useLocaleContext();
const { isMobile } = useMobile();
const { session, connect, payable } = usePaymentContext();
const subscription = useSubscription("events");
const formErrorPosition = "bottom";
const {
control,
getValues,
setValue,
handleSubmit,
formState: { errors },
trigger
} = useFormContext();
const errorRef = useRef(null);
const quantityInventoryStatus = useMemo(() => {
let status = true;
for (const item of checkoutSession.line_items) {
if (formatQuantityInventory(item.price, item.quantity)) {
status = false;
break;
}
}
return status;
}, [checkoutSession]);
const [state, setState] = useSetState({
submitting: false,
paying: false,
paid: false,
paymentIntent,
stripeContext: void 0,
customer,
customerLimited: false,
stripePaying: false,
fastCheckoutInfo: null,
creditInsufficientInfo: null
});
const currencies = flattenPaymentMethods(paymentMethods);
const onCheckoutComplete = useMemoizedFn(async ({ response }) => {
if (response.id === checkoutSession.id && state.paid === false) {
await handleConnected();
}
});
useEffect(() => {
if (subscription) {
subscription.on("checkout.session.completed", onCheckoutComplete);
}
}, [subscription]);
const mergeUserInfo = (customerInfo, userInfo) => {
return {
...userInfo || {},
name: customerInfo?.name || customerInfo?.fullName || userInfo?.name || userInfo?.fullName,
fullName: customerInfo?.name || customerInfo?.fullName || userInfo?.name || userInfo?.fullName,
email: customerInfo?.email || userInfo?.email,
phone: customerInfo?.phone || userInfo?.phone,
address: {
...userInfo?.address || {},
...customerInfo?.address || {},
country: customerInfo?.address?.country || userInfo?.address?.country,
state: customerInfo?.address?.state || userInfo?.address?.province,
line1: customerInfo?.address?.line1 || userInfo?.address?.line1,
line2: customerInfo?.address?.line2 || userInfo?.address?.line2,
city: customerInfo?.address?.city || userInfo?.address?.city,
postal_code: customerInfo?.address?.postal_code || userInfo?.address?.postalCode
},
metadata: {
...userInfo?.metadata || {},
phone: {
country: customerInfo?.address?.country || userInfo?.metadata?.phone?.country,
phoneNumber: customerInfo?.phone || userInfo?.metadata?.phone?.phoneNumber
}
}
};
};
useEffect(() => {
const initUserInfo = async () => {
if (session?.user) {
const values = getValues();
let userInfo = session.user;
try {
const { data: customerInfo } = await api.get("/api/customers/me?skipSummary=1&fallback=1");
userInfo = mergeUserInfo(customerInfo, userInfo);
} catch (err) {
userInfo = mergeUserInfo(customer || {}, userInfo);
console.error(err);
}
setUserFormValues(userInfo, values, setValue, {
preferExisting: false,
showPhone: checkoutSession.phone_number_collection?.enabled
});
} else {
setUserFormValues(
{
name: "",
email: "",
phone: "",
address: {
state: "",
line1: "",
line2: "",
city: "",
postal_code: ""
}
},
{},
setValue,
{ preferExisting: false, showPhone: checkoutSession.phone_number_collection?.enabled }
);
}
};
if (state.submitting) {
return;
}
initUserInfo();
}, [session?.user, checkoutSession.phone_number_collection?.enabled]);
const paymentMethod = useWatch({ control, name: "payment_method" });
const paymentCurrencyId = useWatch({ control, name: "payment_currency" });
const afterUserLoggedIn = useMemoizedFn(() => {
handleSubmit(onFormSubmit, onFormError)();
});
const payee = getStatementDescriptor(checkoutSession.line_items);
let buttonText = "";
if (paymentLink?.donation_settings) {
if (action) {
buttonText = action;
} else {
buttonText = t("payment.checkout.donate");
}
} else {
buttonText = t(`payment.checkout.${checkoutSession.mode}`);
}
buttonText = session?.user || isDonation ? buttonText : t("payment.checkout.connect", { action: buttonText });
const method = paymentMethods.find((x) => x.id === paymentMethod);
const paymentCurrency = currencies.find((x) => x.id === paymentCurrencyId);
const showStake = method.type === "arcblock" && !checkoutSession.subscription_data?.no_stake;
const isDonationMode = checkoutSession?.submit_type === "donate" && isDonation;
const showForm = useMemo(() => {
if (!session?.user) {
return false;
}
if (method.type === "stripe") {
return true;
}
if (checkoutSession.phone_number_collection?.enabled) {
return true;
}
const mode = checkoutSession.billing_address_collection;
if (mode === "required") {
return true;
}
if (session?.user?.fullName && session?.user?.email) {
return false;
}
return true;
}, [session?.user, method, checkoutSession]);
const handleConnected = async () => {
setState({ paying: true });
try {
const result = await waitForCheckoutComplete(checkoutSession.id);
if (state.paid === false) {
setState({ paid: true, paying: false });
onPaid(result);
}
} catch (err) {
Toast.error(formatError(err));
} finally {
setState({ paying: false });
}
};
useEffect(() => {
if (errorRef.current && !isEmpty(errors) && isMobile) {
errorRef.current.scrollIntoView({ behavior: "smooth" });
}
}, [errors, isMobile]);
const onUserLoggedIn = async () => {
const { data: profile } = await api.get("/api/customers/me?fallback=1&skipSummary=1");
if (profile) {
const values = getValues();
const userInfo = mergeUserInfo(profile, session?.user);
setUserFormValues(userInfo, values, setValue, {
preferExisting: false,
showPhone: checkoutSession.phone_number_collection?.enabled,
shouldValidate: true
});
await trigger();
}
};
const handleFastCheckoutConfirm = async () => {
if (!state.fastCheckoutInfo) return;
setState({
fastCheckoutInfo: {
...state.fastCheckoutInfo,
loading: true
}
});
try {
const result = await api.post(`/api/checkout-sessions/${checkoutSession.id}/fast-checkout-confirm`);
if (result.data.fastPaid) {
setState({
fastCheckoutInfo: null,
paying: true
});
await handleConnected();
} else {
Toast.error(t("payment.checkout.fastPay.failed"));
setState({
fastCheckoutInfo: null,
paying: true
});
openConnect();
}
} catch (err) {
console.error(err);
Toast.error(formatError(err));
setState({
fastCheckoutInfo: null
});
}
};
const handleFastCheckoutCancel = () => {
setState({ fastCheckoutInfo: null });
};
const handleCreditInsufficientClose = () => {
setState({ creditInsufficientInfo: null });
};
const openConnect = () => {
try {
if (!["arcblock", "ethereum", "base"].includes(method.type)) {
return;
}
setState({ paying: true });
connect.open({
locale,
containerEl: void 0,
action: checkoutSession.mode,
prefix: joinURL(getPrefix(), "/api/did"),
saveConnect: false,
useSocket: isCrossOrigin() === false,
extraParams: { checkoutSessionId: checkoutSession.id, sessionUserDid: session?.user?.did },
onSuccess: async () => {
connect.close();
await handleConnected();
},
onClose: () => {
connect.close();
setState({ submitting: false, paying: false });
},
onError: (err) => {
console.error(err);
setState({ submitting: false, paying: false });
onError(err);
},
messages: {
title: t("payment.checkout.connectModal.title", { action: buttonText }),
scan: t("payment.checkout.connectModal.scan"),
confirm: t("payment.checkout.connectModal.confirm"),
cancel: t("payment.checkout.connectModal.cancel")
}
});
} catch (err) {
Toast.error(formatError(err));
}
};
const onFormSubmit = async (data) => {
setState({ submitting: true });
try {
let result;
if (isDonationMode) {
result = await api.put(`/api/checkout-sessions/${checkoutSession.id}/donate-submit`, data);
} else {
result = await api.put(`/api/checkout-sessions/${checkoutSession.id}/submit`, data);
}
setState({
paymentIntent: result.data.paymentIntent,
stripeContext: result.data.stripeContext,
customer: result.data.customer,
submitting: false,
customerLimited: false
});
if (["arcblock", "ethereum", "base"].includes(method.type)) {
if (paymentCurrency?.type === "credit") {
if (result.data.creditSufficient === true) {
setState({
fastCheckoutInfo: {
open: true,
loading: false,
sourceType: "credit",
amount: result.data.fastPayInfo?.amount || "0",
payer: result.data.fastPayInfo?.payer,
availableCredit: result.data.fastPayInfo?.amount || "0",
balance: result.data.fastPayInfo?.token?.balance || "0"
}
});
} else {
setState({
creditInsufficientInfo: {
open: true
}
});
}
} else if ((result.data.balance?.sufficient || result.data.delegation?.sufficient) && !isDonationMode && result.data.fastPayInfo) {
setState({
fastCheckoutInfo: {
open: true,
loading: false,
sourceType: result.data.fastPayInfo.type,
amount: result.data.fastPayInfo.amount,
payer: result.data.fastPayInfo.payer
}
});
} else {
openConnect();
}
}
if (["stripe"].includes(method.type)) {
if (result.data.stripeContext?.status === "succeeded") {
setState({ paying: true });
} else {
setTimeout(() => {
setState({ stripePaying: true });
}, 200);
}
}
} catch (err) {
console.error(err);
let shouldToast = true;
if (err.response?.data?.code) {
dispatch(`error.${err.response?.data?.code}`);
if (err.response.data.code === "CUSTOMER_LIMITED") {
shouldToast = false;
setState({ customerLimited: true });
}
}
if (shouldToast) {
Toast.error(formatError(err));
}
} finally {
setState({ submitting: false });
}
};
const onFormError = (err) => {
if (err) {
console.error(err);
}
setState({ submitting: false });
};
const onAction = () => {
if (state.submitting || state.paying) {
return;
}
if (errorRef.current && !isEmpty(errors) && isMobile) {
errorRef.current.scrollIntoView({ behavior: "smooth" });
}
if (session?.user) {
handleSubmit(onFormSubmit, onFormError)();
} else {
if (isDonationMode) {
handleSubmit(onFormSubmit, onFormError)();
return;
}
session?.login(() => {
setState({ submitting: true });
onUserLoggedIn().then(afterUserLoggedIn).catch((err) => {
console.error(err);
setState({ submitting: false });
});
});
}
};
const onStripeConfirm = async () => {
setState({ stripePaying: false, paying: true });
await handleConnected();
};
const onStripeCancel = () => {
setState({ stripePaying: false });
};
useEffect(() => {
const handleKeyDown = (e) => {
if (e.key === "Enter" && !state.submitting && !state.paying && !state.stripePaying && quantityInventoryStatus && payable) {
onAction();
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [state.submitting, state.paying, state.stripePaying, quantityInventoryStatus, payable]);
const balanceLink = getTokenBalanceLink(method, state.fastCheckoutInfo?.payer || "");
const FastCheckoutConfirmDialog = state.fastCheckoutInfo && /* @__PURE__ */ jsx(
ConfirmDialog,
{
onConfirm: handleFastCheckoutConfirm,
onCancel: handleFastCheckoutCancel,
title: state.fastCheckoutInfo.sourceType === "credit" ? t("payment.checkout.fastPay.credit.title") : t("payment.checkout.fastPay.title"),
message: state.fastCheckoutInfo.sourceType === "credit" ? /* @__PURE__ */ jsx(Typography, { children: t("payment.checkout.fastPay.credit.meteringSubscriptionMessage", {
available: `${fromUnitToToken(state.fastCheckoutInfo?.balance || "0", paymentCurrency?.decimal || 18).toString()} ${paymentCurrency?.symbol}`
}) }) : /* @__PURE__ */ jsxs(Stack, { children: [
/* @__PURE__ */ jsx(Typography, { children: t("payment.checkout.fastPay.autoPaymentReason") }),
/* @__PURE__ */ jsx(Divider, { sx: { mt: 1.5, mb: 1.5 } }),
/* @__PURE__ */ jsxs(Stack, { spacing: 1, children: [
/* @__PURE__ */ jsxs(
Stack,
{
sx: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between"
},
children: [
/* @__PURE__ */ jsx(
Typography,
{
sx: {
color: "text.primary",
whiteSpace: "nowrap"
},
children: t("payment.checkout.fastPay.payer")
}
),
/* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
/* @__PURE__ */ jsx(DID, { did: state.fastCheckoutInfo.payer || "", compact: true, responsive: false }),
balanceLink && /* @__PURE__ */ jsx(Tooltip, { title: t("payment.checkout.fastPay.balanceLink"), placement: "top", children: /* @__PURE__ */ jsx(
OpenInNew,
{
sx: {
color: "text.lighter",
fontSize: "0.85rem",
cursor: "pointer",
"&:hover": { color: "text.primary" }
},
onClick: () => {
window.open(balanceLink, "_blank");
}
}
) })
] })
]
}
),
/* @__PURE__ */ jsxs(
Stack,
{
sx: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between"
},
children: [
/* @__PURE__ */ jsx(
Typography,
{
sx: {
color: "text.primary"
},
children: t("payment.checkout.fastPay.amount")
}
),
/* @__PURE__ */ jsxs(Typography, { children: [
fromUnitToToken(state.fastCheckoutInfo.amount, paymentCurrency?.decimal || 18).toString(),
" ",
paymentCurrency?.symbol
] })
]
}
)
] })
] }),
loading: state.fastCheckoutInfo.loading,
color: "primary"
}
);
const CreditInsufficientDialog = state.creditInsufficientInfo && /* @__PURE__ */ jsx(
ConfirmDialog,
{
onConfirm: handleCreditInsufficientClose,
onCancel: handleCreditInsufficientClose,
title: t("payment.checkout.fastPay.credit.insufficientTitle"),
message: /* @__PURE__ */ jsx(Typography, { children: t("payment.checkout.fastPay.credit.insufficientMessage") }),
confirm: t("common.confirm")
}
);
if (onlyShowBtn) {
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(Box, { className: "cko-payment-submit-btn", children: /* @__PURE__ */ jsxs(
Button,
{
variant: "contained",
color: "primary",
size: "large",
className: "cko-submit-button",
onClick: () => {
if (state.submitting || state.paying) {
return;
}
onAction();
},
fullWidth: true,
disabled: state.stripePaying || !quantityInventoryStatus || !payable,
children: [
(state.submitting || state.paying) && /* @__PURE__ */ jsx(CircularProgress, { size: 16, sx: { mr: 0.5, color: "primary.contrastText" } }),
state.submitting || state.paying ? t("payment.checkout.processing") : buttonText
]
}
) }),
state.customerLimited && /* @__PURE__ */ jsx(
OverdueInvoicePayment,
{
customerId: customer?.id || session?.user?.did,
onPaid: () => {
setState({ customerLimited: false });
onAction();
},
alertMessage: t("payment.customer.pastDue.alert.customMessage"),
detailLinkOptions: {
enabled: true,
onClick: () => {
setState({ customerLimited: false });
window.open(
joinURL(
getPrefix(),
`/customer/invoice/past-due?referer=${encodeURIComponent(window.location.href)}`
),
"_self"
);
}
},
dialogProps: {
open: state.customerLimited,
onClose: () => setState({ customerLimited: false }),
title: t("payment.customer.pastDue.alert.title")
}
}
),
FastCheckoutConfirmDialog,
CreditInsufficientDialog
] });
}
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(Stack, { className: "cko-payment-contact", children: [
/* @__PURE__ */ jsx(
Typography,
{
title: t("payment.checkout.paymentDetails"),
sx: {
color: "text.primary",
fontSize: {
xs: "18px",
md: "24px"
},
fontWeight: "700",
lineHeight: "32px",
mb: 2.5
},
children: t("payment.checkout.paymentDetails")
}
),
/* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(
Stack,
{
direction: "column",
className: "cko-payment-methods",
sx: {
alignItems: "flex-start"
},
children: [
/* @__PURE__ */ jsx(Stack, { direction: "row", sx: { width: "100%" }, children: /* @__PURE__ */ jsx(
Controller,
{
name: "payment_currency",
control,
render: ({ field }) => /* @__PURE__ */ jsx(
CurrencySelector,
{
value: field.value,
currencies,
onChange: (id, methodId) => {
field.onChange(id);
setValue("payment_method", methodId);
saveCurrencyPreference(id, session?.user?.did);
}
}
)
}
) }),
state.stripePaying && state.stripeContext && /* @__PURE__ */ jsx(
StripeCheckout,
{
clientSecret: state.stripeContext.client_secret,
intentType: state.stripeContext.intent_type,
publicKey: method.settings.stripe?.publishable_key,
customer: state.customer,
mode: checkoutSession.mode,
onConfirm: onStripeConfirm,
onCancel: onStripeCancel,
returnUrl: checkoutSession?.success_url
}
)
]
}
) }),
showForm && /* @__PURE__ */ jsxs(
Stack,
{
direction: "column",
className: "cko-payment-form",
id: "cko-payment-form",
spacing: 0,
ref: !isEmpty(errors) ? errorRef : void 0,
sx: { flex: 1, overflow: "auto", py: 1 },
children: [
/* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.name") }),
/* @__PURE__ */ jsx(
FormInput,
{
name: "customer_name",
variant: "outlined",
errorPosition: formErrorPosition,
rules: {
required: t("payment.checkout.required"),
...getFieldValidation("customer_name", checkoutSession.metadata?.page_info?.field_validation, locale)
}
}
),
/* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.email") }),
/* @__PURE__ */ jsx(
FormInput,
{
name: "customer_email",
variant: "outlined",
errorPosition: formErrorPosition,
rules: {
required: t("payment.checkout.required"),
validate: (x) => isEmail(x) ? true : t("payment.checkout.invalid"),
...getFieldValidation(
"customer_email",
checkoutSession.metadata?.page_info?.field_validation,
locale
)
}
}
),
checkoutSession.phone_number_collection?.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.phone") }),
/* @__PURE__ */ jsx(
PhoneInput,
{
name: "customer_phone",
variant: "outlined",
errorPosition: formErrorPosition,
placeholder: "Phone number",
rules: {
required: t("payment.checkout.required"),
validate: async (x) => {
const isValid = await validatePhoneNumber(x);
return isValid ? true : t("payment.checkout.invalid");
},
...getFieldValidation(
"customer_phone",
checkoutSession.metadata?.page_info?.field_validation,
locale
)
}
}
)
] }),
/* @__PURE__ */ jsx(
AddressForm,
{
mode: checkoutSession.billing_address_collection,
stripe: method?.type === "stripe",
sx: { marginTop: "0 !important" },
fieldValidation: checkoutSession.metadata?.page_info?.field_validation,
errorPosition: formErrorPosition
}
)
]
}
)
] }) }),
/* @__PURE__ */ jsx(Divider, { sx: { mt: 2.5, mb: 2.5 } }),
/* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(Stack, { className: "cko-payment-submit", children: [
/* @__PURE__ */ jsx(Box, { className: "cko-payment-submit-btn", children: /* @__PURE__ */ jsx(
LoadingButton,
{
variant: "contained",
color: "primary",
size: "large",
className: "cko-submit-button",
onClick: () => {
onAction();
},
fullWidth: true,
loading: state.submitting || state.paying,
disabled: state.stripePaying || !quantityInventoryStatus || !payable,
children: state.submitting || state.paying ? t("payment.checkout.processing") : buttonText
}
) }),
["subscription", "setup"].includes(checkoutSession.mode) && /* @__PURE__ */ jsx(Typography, { sx: { mt: 2.5, color: "text.lighter", fontSize: "0.7875rem", lineHeight: "0.9625rem" }, children: showStake ? t("payment.checkout.confirm.withStake", { payee }) : t("payment.checkout.confirm.withoutStake", { payee }) }),
checkoutSession.metadata?.page_info?.form_purpose_description && /* @__PURE__ */ jsxs(Box, { sx: { mt: 1, display: "flex", alignItems: "center", gap: 0.5 }, children: [
/* @__PURE__ */ jsx(HelpOutline, { sx: { color: "text.lighter", fontSize: "0.75rem" } }),
/* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontSize: "0.75rem", color: "text.lighter" }, children: locale === "zh" ? checkoutSession.metadata.page_info.form_purpose_description.zh : checkoutSession.metadata.page_info.form_purpose_description.en })
] })
] }) }),
state.customerLimited && /* @__PURE__ */ jsx(
OverdueInvoicePayment,
{
customerId: customer?.id || session?.user?.did,
onPaid: () => {
setState({ customerLimited: false });
onAction();
},
alertMessage: t("payment.customer.pastDue.alert.customMessage"),
detailLinkOptions: {
enabled: true,
onClick: () => {
setState({ customerLimited: false });
window.open(
joinURL(getPrefix(), `/customer/invoice/past-due?referer=${encodeURIComponent(window.location.href)}`),
"_self"
);
}
},
dialogProps: {
open: state.customerLimited,
onClose: () => setState({ customerLimited: false }),
title: t("payment.customer.pastDue.alert.title")
}
}
),
FastCheckoutConfirmDialog,
CreditInsufficientDialog
] });
}