@blocklet/payment-react
Version:
Reusable react components for payment kit v2
657 lines (656 loc) • 25.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
module.exports = PaymentSummary;
var _jsxRuntime = require("react/jsx-runtime");
var _context = require("@arcblock/ux/lib/Locale/context");
var _Toast = _interopRequireDefault(require("@arcblock/ux/lib/Toast"));
var _iconsMaterial = require("@mui/icons-material");
var _material = require("@mui/material");
var _util = require("@ocap/util");
var _ahooks = require("ahooks");
var _noop = _interopRequireDefault(require("lodash/noop"));
var _useBus = _interopRequireDefault(require("use-bus"));
var _ExpandMore = _interopRequireDefault(require("@mui/icons-material/ExpandMore"));
var _styles = require("@mui/material/styles");
var _react = require("react");
var _status = _interopRequireDefault(require("../components/status"));
var _api = _interopRequireDefault(require("../libs/api"));
var _util2 = require("../libs/util");
var _productDonation = _interopRequireDefault(require("./product-donation"));
var _productItem = _interopRequireDefault(require("./product-item"));
var _livemode = _interopRequireDefault(require("../components/livemode"));
var _payment = require("../contexts/payment");
var _mobile = require("../hooks/mobile");
var _dynamicPricing = require("../hooks/dynamic-pricing");
var _loadingButton = _interopRequireDefault(require("../components/loading-button"));
var _dynamicPricingUnavailable = _interopRequireDefault(require("../components/dynamic-pricing-unavailable"));
var _promotionSection = _interopRequireDefault(require("./summary-section/promotion-section"));
var _totalSection = _interopRequireDefault(require("./summary-section/total-section"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const ExpandMore = (0, _styles.styled)(props => {
const {
expand,
...other
} = props;
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.IconButton, {
...other
});
})(({
theme,
expand
}) => ({
transform: !expand ? "rotate(0deg)" : "rotate(180deg)",
marginLeft: "auto",
transition: theme.transitions.create("transform", {
duration: theme.transitions.duration.shortest
})
}));
async function fetchCrossSell(id, skipError = true) {
try {
const {
data
} = await _api.default.get(`/api/checkout-sessions/${id}/cross-sell?skipError=${skipError}`);
if (!data.error) {
return data;
}
return null;
} catch (err) {
return null;
}
}
function getStakingSetup(items, currency, billingThreshold = 0) {
const staking = {
licensed: new _util.BN(0),
metered: new _util.BN(0)
};
const recurringItems = items.map(x => x.upsell_price || x.price).filter(x => x.type === "recurring" && x.recurring);
if (recurringItems.length > 0) {
if (+billingThreshold) {
return (0, _util.fromTokenToUnit)(billingThreshold, currency.decimal).toString();
}
items.forEach(x => {
const price = x.upsell_price || x.price;
const unit = (0, _util2.getPriceUintAmountByCurrency)(price, currency);
const amount = new _util.BN(unit).mul(new _util.BN(x.quantity));
if (price.type === "recurring" && price.recurring) {
if (price.recurring.usage_type === "licensed") {
staking.licensed = staking.licensed.add(amount);
}
if (price.recurring.usage_type === "metered") {
staking.metered = staking.metered.add(amount);
}
}
});
return staking.licensed.add(staking.metered).toString();
}
return "0";
}
function PaymentSummary({
items,
currency,
trialInDays,
billingThreshold,
onUpsell = _noop.default,
onDownsell = _noop.default,
onQuantityChange = _noop.default,
onApplyCrossSell = _noop.default,
onCancelCrossSell = _noop.default,
onChangeAmount = _noop.default,
checkoutSessionId = "",
crossSellBehavior = "",
showStaking = false,
donationSettings = void 0,
action = "",
trialEnd = 0,
completed = false,
checkoutSession = void 0,
paymentIntent = void 0,
paymentMethods = [],
onPromotionUpdate = _noop.default,
showFeatures = false,
rateUnavailable = false,
isRateLoading = false,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
rateError: _rateError = void 0,
// Technical errors are logged but not displayed to users
onQuoteExpired = void 0,
onRefreshRate = void 0,
onSlippageChange = void 0,
slippageConfig: slippageConfigProp = void 0,
liveRate = void 0,
liveQuoteSnapshot = void 0,
isStripePayment = false,
isSubscription: isSubscriptionProp = void 0,
...rest
}) {
const {
t,
locale
} = (0, _context.useLocaleContext)();
const {
isMobile
} = (0, _mobile.useMobile)();
const {
paymentState,
...settings
} = (0, _payment.usePaymentContext)();
const [state, setState] = (0, _ahooks.useSetState)({
loading: false,
shake: false,
expanded: items?.length < 3
});
const {
data,
runAsync
} = (0, _ahooks.useRequest)(skipError => checkoutSessionId ? fetchCrossSell(checkoutSessionId, skipError) : Promise.resolve(null));
const sessionDiscounts = checkoutSession?.discounts || [];
const allowPromotionCodes = !!checkoutSession?.allow_promotion_codes;
const hasDiscounts = sessionDiscounts?.length > 0;
const discountCurrency = paymentMethods && checkoutSession ? (0, _util2.findCurrency)(paymentMethods, hasDiscounts ? checkoutSession?.currency_id || currency.id : currency.id) || settings.settings?.baseCurrency : currency;
const slippageConfig = slippageConfigProp ?? checkoutSession?.metadata?.slippage;
const {
hasDynamicPricing,
isPriceLocked,
quoteMeta,
rateInfo,
quoteLockedAt,
currentSlippagePercent,
rateDisplay,
calculatedTokenAmount,
calculatedDiscountAmount,
calculateUsdDisplay,
buildQuoteDetailRows
} = (0, _dynamicPricing.useDynamicPricing)({
items,
currency: discountCurrency,
liveRate,
liveQuoteSnapshot,
checkoutSession,
paymentIntent,
locale,
isStripePayment,
isSubscription: isSubscriptionProp,
slippageConfig,
trialInDays,
trialEnd,
discounts: sessionDiscounts
});
const headlines = (0, _util2.formatCheckoutHeadlines)(items, discountCurrency, {
trialEnd,
trialInDays
}, locale, {
exchangeRate: rateInfo.exchangeRate
});
const staking = showStaking ? getStakingSetup(items, discountCurrency, billingThreshold) : "0";
const effectiveHasDynamicPricing = hasDynamicPricing && !isStripePayment;
const hasRecurringItems = items.some(x => (x.upsell_price || x.price)?.type === "recurring");
const isTrialScenario = headlines.actualAmount === "0" && hasRecurringItems;
const headlineAmountDisplay = (0, _react.useMemo)(() => {
if (!effectiveHasDynamicPricing) {
return headlines.amount;
}
if (isTrialScenario || !headlines.amount.includes(discountCurrency.symbol)) {
return headlines.amount;
}
if (calculatedTokenAmount) {
const displayAmount = (0, _util.fromUnitToToken)(calculatedTokenAmount, discountCurrency?.decimal);
const formatted2 = (0, _util2.formatDynamicPrice)(displayAmount, true, 6);
return `${formatted2} ${discountCurrency.symbol}`;
}
const formatted = (0, _util2.formatDynamicPrice)(headlines.actualAmount, true, 6);
return `${formatted} ${discountCurrency.symbol}`;
}, [headlines.amount, headlines.actualAmount, discountCurrency.symbol, discountCurrency?.decimal, effectiveHasDynamicPricing, calculatedTokenAmount, isTrialScenario]);
const discountAmount = (0, _react.useMemo)(() => new _util.BN(checkoutSession?.total_details?.amount_discount || "0"), [checkoutSession?.total_details?.amount_discount]);
const subtotalAmountUnit = new _util.BN((0, _util.fromTokenToUnit)(headlines.actualAmount, discountCurrency?.decimal)).add(new _util.BN(staking)).toString();
const subtotalAmount = (0, _util.fromUnitToToken)(subtotalAmountUnit, discountCurrency?.decimal);
const totalAmountUnit = new _util.BN(subtotalAmountUnit).sub(discountAmount).toString();
const totalAmountValue = (0, _util.fromUnitToToken)(totalAmountUnit, discountCurrency?.decimal);
const subtotalDisplay = (0, _react.useMemo)(() => {
if (effectiveHasDynamicPricing && calculatedTokenAmount && !isTrialScenario) {
const dynamicSubtotalUnit = new _util.BN(calculatedTokenAmount).add(new _util.BN(staking)).toString();
const displayAmount = (0, _util.fromUnitToToken)(dynamicSubtotalUnit, discountCurrency?.decimal);
return (0, _util2.formatDynamicPrice)(displayAmount, true, 6);
}
return (0, _util2.formatDynamicPrice)(subtotalAmount, effectiveHasDynamicPricing, 6);
}, [effectiveHasDynamicPricing, calculatedTokenAmount, staking, discountCurrency?.decimal, subtotalAmount, isTrialScenario]);
const totalAmountDisplay = (0, _react.useMemo)(() => {
if (effectiveHasDynamicPricing && calculatedTokenAmount && !isTrialScenario) {
const effectiveDiscount = calculatedDiscountAmount ? new _util.BN(calculatedDiscountAmount) : discountAmount;
const dynamicTotalUnit = new _util.BN(calculatedTokenAmount).add(new _util.BN(staking)).sub(effectiveDiscount).toString();
const displayAmount = (0, _util.fromUnitToToken)(dynamicTotalUnit, discountCurrency?.decimal);
const numericValue = Number(displayAmount);
if (Number.isFinite(numericValue) && numericValue >= 0) {
return (0, _util2.formatDynamicPrice)(displayAmount, true, 6);
}
}
if (isStripePayment && calculatedDiscountAmount && !isTrialScenario) {
const effectiveDiscount = new _util.BN(calculatedDiscountAmount);
const adjustedTotalUnit = new _util.BN(subtotalAmountUnit).sub(effectiveDiscount).toString();
const displayAmount = (0, _util.fromUnitToToken)(adjustedTotalUnit, discountCurrency?.decimal);
const numericValue = Number(displayAmount);
if (Number.isFinite(numericValue) && numericValue >= 0) {
return (0, _util2.formatDynamicPrice)(displayAmount, false, 6);
}
}
return (0, _util2.formatDynamicPrice)(totalAmountValue, effectiveHasDynamicPricing, 6);
}, [effectiveHasDynamicPricing, calculatedTokenAmount, staking, discountAmount, calculatedDiscountAmount, discountCurrency?.decimal, totalAmountValue, isTrialScenario, isStripePayment, subtotalAmountUnit]);
const totalAmountText = totalAmountDisplay === "\u2014" ? "\u2014" : `${totalAmountDisplay} ${discountCurrency.symbol}`;
const totalUsdDisplay = (0, _react.useMemo)(() => {
if (effectiveHasDynamicPricing && calculatedTokenAmount && !isTrialScenario) {
const effectiveDiscount = calculatedDiscountAmount ? new _util.BN(calculatedDiscountAmount) : discountAmount;
const dynamicTotalUnit = new _util.BN(calculatedTokenAmount).add(new _util.BN(staking)).sub(effectiveDiscount).toString();
const dynamicTotalToken = (0, _util.fromUnitToToken)(dynamicTotalUnit, discountCurrency?.decimal);
return calculateUsdDisplay(dynamicTotalToken);
}
return calculateUsdDisplay(totalAmountValue);
}, [effectiveHasDynamicPricing, calculatedTokenAmount, staking, discountAmount, calculatedDiscountAmount, discountCurrency?.decimal, totalAmountValue, calculateUsdDisplay, isTrialScenario]);
const quoteDetailRows = buildQuoteDetailRows(t);
const isSubscription = isSubscriptionProp ?? (checkoutSession?.mode === "subscription" || checkoutSession?.mode === "setup");
const handlePromotionUpdate = () => {
onPromotionUpdate?.();
};
const handleRemovePromotion = async sessionId => {
if (paymentState.paying || paymentState.stripePaying) {
return;
}
try {
await _api.default.delete(`/api/checkout-sessions/${sessionId}/remove-promotion`);
onPromotionUpdate?.();
} catch (err) {
console.error("Failed to remove promotion code:", err);
}
};
const expiredHandledRef = (0, _react.useRef)(false);
(0, _react.useEffect)(() => {
if (completed || expiredHandledRef.current) {
return;
}
if (!liveQuoteSnapshot?.expires_at && !quoteMeta?.expiresAt) {
return;
}
const currentTime = Math.floor(Date.now() / 1e3);
const effectiveExpiresAt = liveQuoteSnapshot?.expires_at ?? quoteMeta?.expiresAt;
const quoteRemaining = effectiveExpiresAt ? Math.max(0, effectiveExpiresAt - currentTime) : 0;
const lockRemaining = quoteLockedAt ? Math.max(0, quoteLockedAt + 180 - currentTime) : 0;
const hasExpiry = !!effectiveExpiresAt;
const lockActive = lockRemaining > 0;
if (hasExpiry && !lockActive && quoteRemaining <= 0) {
expiredHandledRef.current = true;
onQuoteExpired?.();
}
}, [liveQuoteSnapshot?.expires_at, quoteMeta?.expiresAt, quoteLockedAt, completed, onQuoteExpired]);
const handleSlippageChange = async newSlippageConfig => {
if (!onSlippageChange) {
return;
}
if (!checkoutSessionId) {
onSlippageChange(newSlippageConfig);
return;
}
try {
await _api.default.put(`/api/checkout-sessions/${checkoutSessionId}/slippage`, {
slippage_config: newSlippageConfig
});
onSlippageChange(newSlippageConfig);
if (onQuoteExpired) {
await onQuoteExpired(true);
}
} catch (err) {
console.error("Failed to update slippage", err);
_Toast.default.error(err.response?.data?.error || (0, _util2.formatError)(err));
}
};
(0, _useBus.default)("error.REQUIRE_CROSS_SELL", () => {
setState({
shake: true
});
setTimeout(() => {
setState({
shake: false
});
}, 1e3);
}, []);
const handleUpsell = async (from, to) => {
await onUpsell(from, to);
runAsync(false);
};
const handleQuantityChange = async (itemId, quantity) => {
await onQuantityChange(itemId, quantity);
runAsync(false);
};
const handleDownsell = async from => {
await onDownsell(from);
runAsync(false);
};
const handleApplyCrossSell = async () => {
if (data) {
try {
setState({
loading: true
});
await onApplyCrossSell(data.id);
} catch (err) {
console.error(err);
} finally {
setState({
loading: false
});
}
}
};
const handleCancelCrossSell = async () => {
try {
setState({
loading: true
});
await onCancelCrossSell();
} catch (err) {
console.error(err);
} finally {
setState({
loading: false
});
}
};
const hasSubTotal = +staking > 0 || allowPromotionCodes;
const ProductCardList = /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
className: "cko-product-list",
sx: {
flex: "0 1 auto",
overflow: "auto"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, {
spacing: {
xs: 1,
sm: 2
},
children: items.map(x => x.price?.custom_unit_amount && onChangeAmount && donationSettings ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_productDonation.default, {
item: x,
settings: donationSettings,
onChange: onChangeAmount,
currency: discountCurrency
}, `${x.price_id}-${discountCurrency.id}`) : /* @__PURE__ */(0, _jsxRuntime.jsx)(_productItem.default, {
item: x,
items,
trialInDays,
trialEnd,
currency: discountCurrency,
exchangeRate: rateInfo.exchangeRate,
isStripePayment,
isPriceLocked,
isRateLoading,
onUpsell: handleUpsell,
onDownsell: handleDownsell,
adjustableQuantity: x.adjustable_quantity,
completed,
showFeatures,
onQuantityChange: handleQuantityChange,
discounts: sessionDiscounts,
calculatedDiscountAmount: calculatedDiscountAmount || (isStripePayment ? discountAmount.toString() : null),
children: x.cross_sell && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
sx: {
alignItems: "center",
justifyContent: "space-between",
width: 1
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_loadingButton.default, {
size: "small",
loadingPosition: "end",
endIcon: null,
color: "error",
variant: "text",
loading: state.loading,
onClick: handleCancelCrossSell,
children: t("payment.checkout.cross_sell.remove")
})]
})
}, `${x.price_id}-${discountCurrency.id}`))
}), data && items.some(x => x.price_id === data.id) === false && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Grow, {
in: true,
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, {
sx: {
mt: 1
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_productItem.default, {
item: {
quantity: 1,
price: data,
price_id: data.id,
cross_sell: true
},
items,
trialInDays,
currency: discountCurrency,
trialEnd,
exchangeRate: rateInfo.exchangeRate,
isStripePayment,
isPriceLocked,
isRateLoading,
onUpsell: _noop.default,
onDownsell: _noop.default,
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
sx: {
alignItems: "center",
justifyContent: "space-between",
width: 1
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
children: crossSellBehavior === "required" && /* @__PURE__ */(0, _jsxRuntime.jsx)(_status.default, {
label: t("payment.checkout.required"),
color: "info",
variant: "outlined",
sx: {
mr: 1
}
})
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_loadingButton.default, {
size: "small",
loadingPosition: "end",
endIcon: null,
color: crossSellBehavior === "required" ? "info" : "info",
variant: crossSellBehavior === "required" ? "text" : "text",
loading: state.loading,
onClick: handleApplyCrossSell,
children: t("payment.checkout.cross_sell.add")
})]
})
})
})
})]
});
if (!discountCurrency || !items?.length) {
return null;
}
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Fade, {
in: true,
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
className: "cko-product",
direction: "column",
...rest,
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
sx: {
display: "flex",
alignItems: "center",
mb: 2.5
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
title: t("payment.checkout.orderSummary"),
sx: {
color: "text.primary",
fontSize: {
xs: "18px",
md: "24px"
},
fontWeight: "700",
lineHeight: "32px"
},
children: action || t("payment.checkout.orderSummary")
}), !settings.livemode && /* @__PURE__ */(0, _jsxRuntime.jsx)(_livemode.default, {})]
}), effectiveHasDynamicPricing && rateUnavailable && /* @__PURE__ */(0, _jsxRuntime.jsx)(_dynamicPricingUnavailable.default, {
sx: {
mb: 2
},
onRetry: onRefreshRate
}), isMobile && !donationSettings ? /* @__PURE__ */(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
onClick: () => setState({
expanded: !state.expanded
}),
sx: {
justifyContent: "space-between",
flexDirection: "row",
alignItems: "center",
mb: 1.5
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
children: t("payment.checkout.productListTotal", {
total: items.length
})
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(ExpandMore, {
expand: state.expanded,
"aria-expanded": state.expanded,
"aria-label": "show more",
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_ExpandMore.default, {})
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Collapse, {
in: state.expanded || !isMobile,
timeout: "auto",
unmountOnExit: true,
children: ProductCardList
})]
}) : ProductCardList, /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Divider, {
sx: {
mt: 2.5,
mb: 2.5
}
}), +staking > 0 && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
spacing: 1,
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
spacing: 1,
sx: {
justifyContent: "space-between",
alignItems: "center"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
spacing: 0.5,
sx: {
alignItems: "center"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
sx: {
color: "text.secondary"
},
children: t("payment.checkout.paymentRequired")
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Tooltip, {
title: t("payment.checkout.stakingConfirm"),
placement: "top",
sx: {
maxWidth: "150px"
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.HelpOutline, {
fontSize: "small",
sx: {
color: "text.lighter"
}
})
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
children: headlineAmountDisplay
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
spacing: 1,
sx: {
justifyContent: "space-between",
alignItems: "center"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
spacing: 0.5,
sx: {
alignItems: "center"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
sx: {
color: "text.secondary"
},
children: t("payment.checkout.staking.title")
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Tooltip, {
title: t("payment.checkout.staking.tooltip"),
placement: "top",
sx: {
maxWidth: "150px"
},
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.HelpOutline, {
fontSize: "small",
sx: {
color: "text.lighter"
}
})
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
children: [(0, _util2.formatAmount)(staking, discountCurrency.decimal), " ", discountCurrency.symbol]
})]
})]
}), (allowPromotionCodes || hasDiscounts) && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
direction: "row",
spacing: 1,
sx: {
justifyContent: "space-between",
alignItems: "center",
...(+staking > 0 && {
borderTop: "1px solid",
borderColor: "divider",
pt: 1,
mt: 1
})
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
className: "base-label",
children: t("common.subtotal")
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
children: [subtotalDisplay, " ", discountCurrency.symbol]
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_promotionSection.default, {
checkoutSessionId: checkoutSession?.id || checkoutSessionId,
currency: discountCurrency,
currencyId: currency.id,
discounts: sessionDiscounts,
allowPromotionCodes,
completed,
disabled: paymentState.paying || paymentState.stripePaying,
onPromotionUpdate: handlePromotionUpdate,
onRemovePromotion: handleRemovePromotion,
calculatedDiscountAmount: calculatedDiscountAmount || (isStripePayment ? discountAmount.toString() : null),
isRateLoading
}), hasSubTotal && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Divider, {
sx: {
my: 1
}
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_totalSection.default, {
totalAmountText,
totalUsdDisplay,
currency: discountCurrency,
hasDynamicPricing,
rateDisplay,
rateInfo,
quoteDetailRows,
currentSlippagePercent,
slippageConfig,
isPriceLocked,
isSubscription,
completed,
onSlippageChange: onSlippageChange ? handleSlippageChange : void 0,
isStripePayment,
isRateLoading,
thenInfo: headlines.thenValue && headlines.showThen ? headlines.thenValue : void 0
})]
})
});
}