@blocklet/payment-react
Version:
Reusable react components for payment kit v2
1,163 lines (1,162 loc) • 42 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
module.exports = PaymentForm;
exports.waitForCheckoutComplete = exports.hasDidWallet = void 0;
var _jsxRuntime = require("react/jsx-runtime");
require("react-international-phone/style.css");
var _context = require("@arcblock/ux/lib/Locale/context");
var _reactInternationalPhone = require("react-international-phone");
var _Toast = _interopRequireDefault(require("@arcblock/ux/lib/Toast"));
var _material = require("@mui/material");
var _ahooks = require("ahooks");
var _pWaitFor = _interopRequireDefault(require("p-wait-for"));
var _react = require("react");
var _reactHookForm = require("react-hook-form");
var _ufo = require("ufo");
var _useBus = require("use-bus");
var _isEmail = _interopRequireDefault(require("validator/es/lib/isEmail"));
var _util = require("@ocap/util");
var _DID = _interopRequireDefault(require("@arcblock/ux/lib/DID"));
var _isEmpty = _interopRequireDefault(require("lodash/isEmpty"));
var _iconsMaterial = require("@mui/icons-material");
var _withTracker = require("@arcblock/ux/lib/withTracker");
var _input = _interopRequireDefault(require("../../components/input"));
var _label = _interopRequireDefault(require("../../components/label"));
var _payment = require("../../contexts/payment");
var _subscription = require("../../hooks/subscription");
var _api = _interopRequireDefault(require("../../libs/api"));
var _util2 = require("../../libs/util");
var _address = _interopRequireDefault(require("./address"));
var _currency = _interopRequireDefault(require("./currency"));
var _phone = _interopRequireDefault(require("./phone"));
var _stripe = _interopRequireDefault(require("./stripe"));
var _mobile = require("../../hooks/mobile");
var _phoneValidator = require("../../libs/phone-validator");
var _loadingButton = _interopRequireDefault(require("../../components/loading-button"));
var _overDueInvoicePayment = _interopRequireDefault(require("../../components/over-due-invoice-payment"));
var _currency2 = require("../../libs/currency");
var _confirm = _interopRequireDefault(require("../../components/confirm"));
var _validator = require("../../libs/validator");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const waitForCheckoutComplete = async sessionId => {
let result;
await (0, _pWaitFor.default)(async () => {
const {
data
} = await _api.default.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;
};
exports.waitForCheckoutComplete = waitForCheckoutComplete;
const hasDidWallet = user => {
const connected = user?.connectedAccounts || user?.extraConfigs?.connectedAccounts || [];
return connected.some(x => x.provider === "wallet");
};
exports.hasDidWallet = hasDidWallet;
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: (0, _phoneValidator.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
};
const updatedFields = {
...currentValues,
billing_address: {
...currentValues.billing_address
}
};
Object.entries(allFields).forEach(([field, value]) => {
if (!preferExisting || !currentValues[field.split(".")[0]]) {
setValue(field, value, {
shouldValidate
});
if (field.startsWith("billing_address.")) {
const addressKey = field.replace("billing_address.", "");
updatedFields.billing_address[addressKey] = value;
} else {
updatedFields[field] = value;
}
}
});
return updatedFields;
};
function PaymentForm({
checkoutSession,
paymentMethods,
paymentIntent,
paymentLink,
customer,
onPaid,
onError,
// mode,
action,
onlyShowBtn = false,
isDonation = false
}) {
const {
t,
locale
} = (0, _context.useLocaleContext)();
const {
isMobile
} = (0, _mobile.useMobile)();
const {
session,
connect,
payable,
setPaymentState
} = (0, _payment.usePaymentContext)();
const subscription = (0, _subscription.useSubscription)("events");
const formErrorPosition = "bottom";
const {
control,
getValues,
setValue,
handleSubmit,
formState: {
errors
},
trigger
} = (0, _reactHookForm.useFormContext)();
const errorRef = (0, _react.useRef)(null);
const processingRef = (0, _react.useRef)(false);
const quantityInventoryStatus = (0, _react.useMemo)(() => {
let status = true;
for (const item of checkoutSession.line_items) {
if ((0, _util2.formatQuantityInventory)(item.price, item.quantity)) {
status = false;
break;
}
}
return status;
}, [checkoutSession]);
const [state, setState] = (0, _ahooks.useSetState)({
submitting: false,
paying: false,
paid: false,
paymentIntent,
stripeContext: void 0,
customer,
customerLimited: false,
stripePaying: false,
fastCheckoutInfo: null,
creditInsufficientInfo: null,
showEditForm: false
});
const currencies = (0, _util2.flattenPaymentMethods)(paymentMethods);
const searchParams = (0, _util2.getQueryParams)(window.location.href);
const onCheckoutComplete = (0, _ahooks.useMemoizedFn)(async ({
response
}) => {
if (response.id === checkoutSession.id && state.paid === false) {
await handleConnected();
}
});
(0, _react.useEffect)(() => {
if (subscription) {
subscription.on("checkout.session.completed", onCheckoutComplete);
}
}, [subscription]);
(0, _react.useEffect)(() => {
setPaymentState({
paying: state.submitting || state.paying,
stripePaying: state.stripePaying
});
}, [state.submitting, state.paying, state.stripePaying]);
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
}
}
};
};
(0, _react.useEffect)(() => {
const initUserInfo = async () => {
if (session?.user) {
const values = getValues();
let userInfo = session.user;
try {
const {
data: customerInfo
} = await _api.default.get("/api/customers/me?skipSummary=1&fallback=1");
userInfo = mergeUserInfo(customerInfo, userInfo);
} catch (err) {
userInfo = mergeUserInfo(customer || {}, userInfo);
console.error(err);
}
const formValues = setUserFormValues(userInfo, values, setValue, {
preferExisting: false,
showPhone: checkoutSession.phone_number_collection?.enabled
});
const isValid = validateUserInfo(formValues);
setState({
showEditForm: !isValid
});
} 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 = (0, _reactHookForm.useWatch)({
control,
name: "payment_method"
});
const paymentCurrencyId = (0, _reactHookForm.useWatch)({
control,
name: "payment_currency"
});
const afterUserLoggedIn = (0, _ahooks.useMemoizedFn)(() => {
handleSubmit(onFormSubmit, onFormError)();
});
const payee = (0, _util2.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 validateUserInfo = values => {
if (!values) {
return false;
}
const fieldValidation = checkoutSession.metadata?.page_info?.field_validation;
const customerName2 = values.customer_name;
if (!customerName2 || customerName2.trim() === "") {
return false;
}
const customerEmail2 = values.customer_email;
if (!customerEmail2 || !(0, _isEmail.default)(customerEmail2)) {
return false;
}
const nameValidation = (0, _validator.getFieldValidation)("customer_name", fieldValidation, locale);
if (nameValidation.pattern) {
const pattern = nameValidation.pattern.value;
if (!pattern.test(customerName2)) {
return false;
}
}
const emailValidation = (0, _validator.getFieldValidation)("customer_email", fieldValidation, locale);
if (emailValidation.pattern) {
const pattern = emailValidation.pattern.value;
if (!pattern.test(customerEmail2)) {
return false;
}
}
const billingAddress2 = values.billing_address || {};
const {
postal_code: postalCode,
country,
state: stateValue,
line1,
city
} = billingAddress2;
if (!postalCode || !(0, _validator.validatePostalCode)(postalCode, country)) {
return false;
}
const postalCodeValidation = (0, _validator.getFieldValidation)("billing_address.postal_code", fieldValidation, locale);
if (postalCodeValidation.pattern) {
const pattern = postalCodeValidation.pattern.value;
if (!pattern.test(postalCode)) {
return false;
}
}
if (!stateValue) {
return false;
}
const stateValidation = (0, _validator.getFieldValidation)("billing_address.state", fieldValidation, locale);
if (stateValidation.pattern) {
const pattern = stateValidation.pattern.value;
if (!pattern.test(stateValue)) {
return false;
}
}
if (checkoutSession.phone_number_collection?.enabled) {
const customerPhone2 = values.customer_phone;
if (!customerPhone2 || customerPhone2.trim() === "") {
return false;
}
const phoneValidation = (0, _validator.getFieldValidation)("customer_phone", fieldValidation, locale);
if (phoneValidation.pattern) {
const pattern = phoneValidation.pattern.value;
if (!pattern.test(customerPhone2)) {
return false;
}
}
}
const addressMode = checkoutSession.billing_address_collection;
if (addressMode === "required") {
if (!country || !stateValue || !line1 || !city || !postalCode) {
return false;
}
const line1Validation = (0, _validator.getFieldValidation)("billing_address.line1", fieldValidation, locale);
if (line1Validation.pattern) {
const pattern = line1Validation.pattern.value;
if (!pattern.test(line1)) {
return false;
}
}
const cityValidation = (0, _validator.getFieldValidation)("billing_address.city", fieldValidation, locale);
if (cityValidation.pattern) {
const pattern = cityValidation.pattern.value;
if (!pattern.test(city)) {
return false;
}
}
}
return true;
};
const customerName = (0, _reactHookForm.useWatch)({
control,
name: "customer_name"
});
const customerEmail = (0, _reactHookForm.useWatch)({
control,
name: "customer_email"
});
const customerPhone = (0, _reactHookForm.useWatch)({
control,
name: "customer_phone"
});
const billingAddress = (0, _reactHookForm.useWatch)({
control,
name: "billing_address"
});
const showForm = session?.user ? state.showEditForm : false;
const handleConnected = async () => {
if (processingRef.current) {
return;
}
processingRef.current = true;
setState({
paying: true
});
try {
const result = await waitForCheckoutComplete(checkoutSession.id);
if (state.paid === false) {
setState({
paid: true,
paying: false
});
onPaid(result);
}
} catch (err) {
const errorMessage = (0, _util2.formatError)(err);
const payFailedEvent = {
action: "payFailed",
// @ts-ignore 后续升级的话就会报错了,移除这个 lint 即可
mode: checkoutSession.mode,
errorMessage,
success: false
};
_withTracker.ReactGA.event(payFailedEvent.action, payFailedEvent);
_Toast.default.error(errorMessage);
} finally {
setState({
paying: false
});
processingRef.current = false;
}
};
(0, _react.useEffect)(() => {
if (errorRef.current && !(0, _isEmpty.default)(errors) && isMobile) {
errorRef.current.scrollIntoView({
behavior: "smooth"
});
}
}, [errors, isMobile]);
const onUserLoggedIn = async () => {
const {
data: profile
} = await _api.default.get("/api/customers/me?fallback=1&skipSummary=1");
if (profile) {
const values = getValues();
const userInfo = mergeUserInfo(profile, session?.user);
const formValues = setUserFormValues(userInfo, values, setValue, {
preferExisting: false,
showPhone: checkoutSession.phone_number_collection?.enabled,
shouldValidate: true
});
const isValid = validateUserInfo(formValues);
setState({
showEditForm: !isValid
});
await trigger();
}
};
const handleFastCheckoutConfirm = async () => {
if (!state.fastCheckoutInfo) return;
setState({
fastCheckoutInfo: {
...state.fastCheckoutInfo,
loading: true
}
});
try {
const result = await _api.default.post(`/api/checkout-sessions/${checkoutSession.id}/fast-checkout-confirm`);
if (result.data.fastPaid) {
setState({
fastCheckoutInfo: null,
paying: true
});
await handleConnected();
} else {
_Toast.default.error(t("payment.checkout.fastPay.failed"));
setState({
fastCheckoutInfo: null,
paying: true
});
openConnect();
}
} catch (err) {
console.error(err);
_Toast.default.error((0, _util2.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: (0, _ufo.joinURL)((0, _util2.getPrefix)(), "/api/did"),
saveConnect: false,
useSocket: (0, _util2.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.default.error((0, _util2.formatError)(err));
}
};
const onFormSubmit = async data => {
const userInfo = session.user;
if (!userInfo.sourceAppPid) {
const hasVendorConfig = checkoutSession.line_items?.some(item => !!item?.price?.product?.vendor_config?.length);
if (hasVendorConfig) {
_Toast.default.error(t("payment.checkout.vendor.accountRequired"));
return;
}
}
setState({
submitting: true
});
try {
let result;
if (isDonationMode) {
result = await _api.default.put(`/api/checkout-sessions/${checkoutSession.id}/donate-submit`, data);
} else {
result = await _api.default.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) {
(0, _useBus.dispatch)(`error.${err.response?.data?.code}`);
if (err.response?.data?.code === "UNIFIED_APP_REQUIRED") {
shouldToast = false;
_Toast.default.error(t("payment.checkout.vendor.accountRequired"));
}
if (err.response.data.code === "CUSTOMER_LIMITED") {
shouldToast = false;
setState({
customerLimited: true
});
}
}
if (shouldToast) {
_Toast.default.error((0, _util2.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 && !(0, _isEmpty.default)(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 = async () => {
try {
await _api.default.post(`/api/checkout-sessions/${checkoutSession.id}/abort-stripe`);
} catch (err) {
console.error(err);
_Toast.default.error((0, _util2.formatError)(err));
} finally {
setState({
stripePaying: false
});
}
};
(0, _react.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 = (0, _util2.getTokenBalanceLink)(method, state.fastCheckoutInfo?.payer || "");
const FastCheckoutConfirmDialog = state.fastCheckoutInfo && /* @__PURE__ */(0, _jsxRuntime.jsx)(_confirm.default, {
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__ */(0, _jsxRuntime.jsx)(_material.Typography, {
children: t("payment.checkout.fastPay.credit.meteringSubscriptionMessage", {
available: `${(0, _util.fromUnitToToken)(state.fastCheckoutInfo?.balance || "0", paymentCurrency?.decimal || 18).toString()} ${paymentCurrency?.symbol}`
})
}) : /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
children: t("payment.checkout.fastPay.autoPaymentReason")
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Divider, {
sx: {
mt: 1.5,
mb: 1.5
}
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
spacing: 1,
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
sx: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
sx: {
color: "text.primary",
whiteSpace: "nowrap"
},
children: t("payment.checkout.fastPay.payer")
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
sx: {
display: "flex",
alignItems: "center",
gap: 0.5
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_DID.default, {
did: state.fastCheckoutInfo.payer || "",
compact: true,
responsive: false
}), balanceLink && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Tooltip, {
title: t("payment.checkout.fastPay.balanceLink"),
placement: "top",
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.OpenInNew, {
sx: {
color: "text.lighter",
fontSize: "0.85rem",
cursor: "pointer",
"&:hover": {
color: "text.primary"
}
},
onClick: () => {
window.open(balanceLink, "_blank");
}
})
})]
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
sx: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
sx: {
color: "text.primary"
},
children: t("payment.checkout.fastPay.amount")
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
children: [(0, _util.fromUnitToToken)(state.fastCheckoutInfo.amount, paymentCurrency?.decimal || 18).toString(), " ", paymentCurrency?.symbol]
})]
})]
})]
}),
loading: state.fastCheckoutInfo.loading,
color: "primary"
});
const CreditInsufficientDialog = state.creditInsufficientInfo && /* @__PURE__ */(0, _jsxRuntime.jsx)(_confirm.default, {
onConfirm: handleCreditInsufficientClose,
onCancel: handleCreditInsufficientClose,
title: t("payment.checkout.fastPay.credit.insufficientTitle"),
message: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
children: t("payment.checkout.fastPay.credit.insufficientMessage")
}),
confirm: t("common.confirm")
});
const getRedirectUrl = () => {
if (searchParams.redirect) {
return decodeURIComponent(searchParams.redirect);
}
if (checkoutSession.success_url) {
return checkoutSession.success_url;
}
if (paymentLink?.after_completion?.redirect?.url) {
return paymentLink.after_completion.redirect.url;
}
return void 0;
};
if (onlyShowBtn) {
return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
className: "cko-payment-submit-btn",
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.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__ */(0, _jsxRuntime.jsx)(_material.CircularProgress, {
size: 16,
sx: {
mr: 0.5,
color: "primary.contrastText"
}
}), state.submitting || state.paying ? t("payment.checkout.processing") : buttonText]
})
}), state.customerLimited && /* @__PURE__ */(0, _jsxRuntime.jsx)(_overDueInvoicePayment.default, {
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((0, _ufo.joinURL)((0, _util2.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__ */(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Fade, {
in: true,
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
className: "cko-payment-contact",
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.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__ */(0, _jsxRuntime.jsx)(_material.Fade, {
in: true,
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "column",
className: "cko-payment-methods",
sx: {
alignItems: "flex-start"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, {
direction: "row",
sx: {
width: "100%"
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_reactHookForm.Controller, {
name: "payment_currency",
control,
render: ({
field
}) => /* @__PURE__ */(0, _jsxRuntime.jsx)(_currency.default, {
value: field.value,
currencies,
onChange: (id, methodId) => {
field.onChange(id);
setValue("payment_method", methodId);
(0, _currency2.saveCurrencyPreference)(id, session?.user?.did);
}
})
})
}), state.stripePaying && state.stripeContext && /* @__PURE__ */(0, _jsxRuntime.jsx)(_stripe.default, {
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: getRedirectUrl()
})]
})
}), !showForm && session?.user && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
spacing: 1.25,
sx: {
mt: 2,
p: 2,
pt: 1,
backgroundColor: "background.paper",
borderRadius: 1,
border: "1px solid",
borderColor: "divider"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
justifyContent: "space-between",
alignItems: "center",
sx: {
mb: 0.25
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "subtitle2",
sx: {
color: "text.primary",
fontSize: "0.875rem"
},
children: t("payment.checkout.customerInfo")
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, {
size: "small",
variant: "text",
onClick: () => setState({
showEditForm: true
}),
sx: {
minWidth: 0
},
children: t("common.edit")
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
spacing: 0.5,
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "body2",
sx: {
color: "text.primary",
fontWeight: 600,
fontSize: "0.9375rem"
},
children: customerName
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "body2",
sx: {
color: "text.secondary",
fontSize: "0.8125rem"
},
children: customerEmail
}), checkoutSession.phone_number_collection?.enabled && customerPhone && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
variant: "body2",
sx: {
color: "text.secondary",
fontSize: "0.8125rem"
},
children: customerPhone
}), billingAddress && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
alignItems: "center",
spacing: 0.75,
children: [billingAddress.country && /* @__PURE__ */(0, _jsxRuntime.jsx)(_reactInternationalPhone.FlagEmoji, {
iso2: billingAddress.country.toLowerCase(),
style: {
width: 18,
height: 14
}
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
variant: "body2",
sx: {
color: "text.secondary",
fontSize: "0.8125rem"
},
children: [checkoutSession.billing_address_collection === "required" ? [billingAddress.line1, billingAddress.city, billingAddress.state].filter(Boolean).join(", ") : billingAddress.state || "", billingAddress.postal_code && ` [ ${t("payment.checkout.billing.postal_code")}: ${billingAddress.postal_code} ]`]
})]
})]
})]
}), showForm && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "column",
className: "cko-payment-form",
id: "cko-payment-form",
spacing: 0,
ref: !(0, _isEmpty.default)(errors) ? errorRef : void 0,
sx: {
flex: 1,
overflow: "auto",
py: 1
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_label.default, {
className: "base-label",
children: t("payment.checkout.customer.name")
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_input.default, {
name: "customer_name",
variant: "outlined",
errorPosition: formErrorPosition,
rules: {
required: t("payment.checkout.required"),
...(0, _validator.getFieldValidation)("customer_name", checkoutSession.metadata?.page_info?.field_validation, locale)
}
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_label.default, {
className: "base-label",
children: t("payment.checkout.customer.email")
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_input.default, {
name: "customer_email",
variant: "outlined",
errorPosition: formErrorPosition,
rules: {
required: t("payment.checkout.required"),
validate: x => (0, _isEmail.default)(x) ? true : t("payment.checkout.invalid"),
...(0, _validator.getFieldValidation)("customer_email", checkoutSession.metadata?.page_info?.field_validation, locale)
}
}), checkoutSession.phone_number_collection?.enabled && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_label.default, {
className: "base-label",
children: t("payment.checkout.customer.phone")
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_phone.default, {
name: "customer_phone",
variant: "outlined",
errorPosition: formErrorPosition,
placeholder: "Phone number",
rules: {
required: t("payment.checkout.required"),
validate: async x => {
const isValid = await (0, _phoneValidator.validatePhoneNumber)(x);
return isValid ? true : t("payment.checkout.invalid");
},
...(0, _validator.getFieldValidation)("customer_phone", checkoutSession.metadata?.page_info?.field_validation, locale)
}
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_address.default, {
mode: checkoutSession.billing_address_collection,
sx: {
marginTop: "0 !important"
},
fieldValidation: checkoutSession.metadata?.page_info?.field_validation,
errorPosition: formErrorPosition
})]
})]
})
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Divider, {
sx: {
mt: 2.5,
mb: 2.5
}
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Fade, {
in: true,
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
className: "cko-payment-submit",
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
className: "cko-payment-submit-btn",
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_loadingButton.default, {
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__ */(0, _jsxRuntime.jsx)(_material.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__ */(0, _jsxRuntime.jsxs)(_material.Box, {
sx: {
mt: 1,
display: "flex",
alignItems: "center",
gap: 0.5
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.HelpOutline, {
sx: {
color: "text.lighter",
fontSize: "0.75rem"
}
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.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__ */(0, _jsxRuntime.jsx)(_overDueInvoicePayment.default, {
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((0, _ufo.joinURL)((0, _util2.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]
});
}