@blocklet/payment-react
Version:
Reusable react components for payment kit v2
433 lines (432 loc) • 14.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 _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 _status = _interopRequireDefault(require("../components/status"));
var _api = _interopRequireDefault(require("../libs/api"));
var _util2 = require("../libs/util");
var _amount = _interopRequireDefault(require("./amount"));
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 _loadingButton = _interopRequireDefault(require("../components/loading-button"));
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) {
try {
const {
data
} = await _api.default.get(`/api/checkout-sessions/${id}/cross-sell`);
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,
...rest
}) {
const {
t,
locale
} = (0, _context.useLocaleContext)();
const {
isMobile
} = (0, _mobile.useMobile)();
const settings = (0, _payment.usePaymentContext)();
const [state, setState] = (0, _ahooks.useSetState)({
loading: false,
shake: false,
expanded: items?.length < 3
});
const {
data,
runAsync
} = (0, _ahooks.useRequest)(() => checkoutSessionId ? fetchCrossSell(checkoutSessionId) : Promise.resolve(null));
const headlines = (0, _util2.formatCheckoutHeadlines)(items, currency, {
trialEnd,
trialInDays
}, locale);
const staking = showStaking ? getStakingSetup(items, currency, billingThreshold) : "0";
const totalAmount = (0, _util.fromUnitToToken)(new _util.BN((0, _util.fromTokenToUnit)(headlines.actualAmount, currency?.decimal)).add(new _util.BN(staking)).toString(), currency?.decimal);
(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();
};
const handleQuantityChange = async (itemId, quantity) => {
await onQuantityChange(itemId, quantity);
runAsync();
};
const handleDownsell = async from => {
await onDownsell(from);
runAsync();
};
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 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
}, `${x.price_id}-${currency.id}`) : /* @__PURE__ */(0, _jsxRuntime.jsx)(_productItem.default, {
item: x,
items,
trialInDays,
trialEnd,
currency,
onUpsell: handleUpsell,
onDownsell: handleDownsell,
adjustableQuantity: x.adjustable_quantity,
completed,
onQuantityChange: handleQuantityChange,
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}-${currency.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,
trialEnd,
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")
})]
})
})
})
})]
});
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, {})]
}), 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)(_jsxRuntime.Fragment, {
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: headlines.amount
})]
}), /* @__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, currency.decimal), " ", currency.symbol]
})]
})]
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
sx: {
display: "flex",
justifyContent: "space-between",
flexDirection: "row",
alignItems: "center",
width: "100%"
},
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
className: "base-label",
children: [t("common.total"), " "]
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_amount.default, {
amount: `${totalAmount} ${currency.symbol}`,
sx: {
fontSize: "16px"
}
})]
}), headlines.then && headlines.showThen && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
component: "div",
sx: {
fontSize: "0.7875rem",
color: "text.lighter",
textAlign: "right",
margin: "-2px 0 8px"
},
children: headlines.then
})]
})
});
}