@blocklet/payment-react 1.25.10 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/checkout-v2/checkout-v2.d.ts +2 -0
- package/es/checkout-v2/checkout-v2.js +121 -0
- package/es/checkout-v2/components/dialogs/checkout-dialogs.d.ts +1 -0
- package/es/checkout-v2/components/dialogs/checkout-dialogs.js +106 -0
- package/es/checkout-v2/components/left/billing-toggle.d.ts +6 -0
- package/es/checkout-v2/components/left/billing-toggle.js +118 -0
- package/es/checkout-v2/components/left/cross-sell-card.d.ts +10 -0
- package/es/checkout-v2/components/left/cross-sell-card.js +167 -0
- package/es/checkout-v2/components/left/product-item-card.d.ts +26 -0
- package/es/checkout-v2/components/left/product-item-card.js +571 -0
- package/es/checkout-v2/components/left/promotion-input.d.ts +19 -0
- package/es/checkout-v2/components/left/promotion-input.js +178 -0
- package/es/checkout-v2/components/left/staking-breakdown.d.ts +9 -0
- package/es/checkout-v2/components/left/staking-breakdown.js +48 -0
- package/es/checkout-v2/components/left/trial-info.d.ts +13 -0
- package/es/checkout-v2/components/left/trial-info.js +48 -0
- package/es/checkout-v2/components/right/currency-grid.d.ts +8 -0
- package/es/checkout-v2/components/right/currency-grid.js +48 -0
- package/es/checkout-v2/components/right/customer-info-card.d.ts +17 -0
- package/es/checkout-v2/components/right/customer-info-card.js +156 -0
- package/es/checkout-v2/components/right/status-feedback.d.ts +7 -0
- package/es/checkout-v2/components/right/status-feedback.js +17 -0
- package/es/checkout-v2/components/right/submit-button.d.ts +10 -0
- package/es/checkout-v2/components/right/submit-button.js +29 -0
- package/es/checkout-v2/components/right/subscription-disclaimer.d.ts +11 -0
- package/es/checkout-v2/components/right/subscription-disclaimer.js +8 -0
- package/es/checkout-v2/components/shared/exchange-rate-footer.d.ts +23 -0
- package/es/checkout-v2/components/shared/exchange-rate-footer.js +182 -0
- package/es/checkout-v2/components/shared/scenario-badge.d.ts +6 -0
- package/es/checkout-v2/components/shared/scenario-badge.js +47 -0
- package/es/checkout-v2/components/shared/total-display.d.ts +7 -0
- package/es/checkout-v2/components/shared/total-display.js +84 -0
- package/es/checkout-v2/index.d.ts +2 -0
- package/es/checkout-v2/index.js +1 -0
- package/es/checkout-v2/layouts/checkout-layout.d.ts +7 -0
- package/es/checkout-v2/layouts/checkout-layout.js +226 -0
- package/es/checkout-v2/panels/left/composite-panel.d.ts +1 -0
- package/es/checkout-v2/panels/left/composite-panel.js +423 -0
- package/es/checkout-v2/panels/left/credit-topup-panel.d.ts +1 -0
- package/es/checkout-v2/panels/left/credit-topup-panel.js +615 -0
- package/es/checkout-v2/panels/left/scenario-router.d.ts +1 -0
- package/es/checkout-v2/panels/left/scenario-router.js +19 -0
- package/es/checkout-v2/panels/right/payment-panel.d.ts +1 -0
- package/es/checkout-v2/panels/right/payment-panel.js +644 -0
- package/es/checkout-v2/types.d.ts +15 -0
- package/es/checkout-v2/types.js +0 -0
- package/es/checkout-v2/utils/format.d.ts +59 -0
- package/es/checkout-v2/utils/format.js +125 -0
- package/es/checkout-v2/utils/scenario-detector.d.ts +3 -0
- package/es/checkout-v2/utils/scenario-detector.js +17 -0
- package/es/checkout-v2/views/error-view.d.ts +7 -0
- package/es/checkout-v2/views/error-view.js +269 -0
- package/es/checkout-v2/views/loading-view.d.ts +5 -0
- package/es/checkout-v2/views/loading-view.js +158 -0
- package/es/checkout-v2/views/success-view.d.ts +29 -0
- package/es/checkout-v2/views/success-view.js +614 -0
- package/es/components/phone-field.d.ts +14 -0
- package/es/components/phone-field.js +96 -0
- package/es/index.d.ts +3 -1
- package/es/index.js +3 -1
- package/es/locales/en.js +45 -6
- package/es/locales/zh.js +45 -6
- package/es/payment/form/index.js +10 -1
- package/lib/checkout-v2/checkout-v2.d.ts +2 -0
- package/lib/checkout-v2/checkout-v2.js +151 -0
- package/lib/checkout-v2/components/dialogs/checkout-dialogs.d.ts +1 -0
- package/lib/checkout-v2/components/dialogs/checkout-dialogs.js +131 -0
- package/lib/checkout-v2/components/left/billing-toggle.d.ts +6 -0
- package/lib/checkout-v2/components/left/billing-toggle.js +126 -0
- package/lib/checkout-v2/components/left/cross-sell-card.d.ts +10 -0
- package/lib/checkout-v2/components/left/cross-sell-card.js +257 -0
- package/lib/checkout-v2/components/left/product-item-card.d.ts +26 -0
- package/lib/checkout-v2/components/left/product-item-card.js +738 -0
- package/lib/checkout-v2/components/left/promotion-input.d.ts +19 -0
- package/lib/checkout-v2/components/left/promotion-input.js +220 -0
- package/lib/checkout-v2/components/left/staking-breakdown.d.ts +9 -0
- package/lib/checkout-v2/components/left/staking-breakdown.js +96 -0
- package/lib/checkout-v2/components/left/trial-info.d.ts +13 -0
- package/lib/checkout-v2/components/left/trial-info.js +82 -0
- package/lib/checkout-v2/components/right/currency-grid.d.ts +8 -0
- package/lib/checkout-v2/components/right/currency-grid.js +96 -0
- package/lib/checkout-v2/components/right/customer-info-card.d.ts +17 -0
- package/lib/checkout-v2/components/right/customer-info-card.js +246 -0
- package/lib/checkout-v2/components/right/status-feedback.d.ts +7 -0
- package/lib/checkout-v2/components/right/status-feedback.js +30 -0
- package/lib/checkout-v2/components/right/submit-button.d.ts +10 -0
- package/lib/checkout-v2/components/right/submit-button.js +35 -0
- package/lib/checkout-v2/components/right/subscription-disclaimer.d.ts +11 -0
- package/lib/checkout-v2/components/right/subscription-disclaimer.js +33 -0
- package/lib/checkout-v2/components/shared/exchange-rate-footer.d.ts +23 -0
- package/lib/checkout-v2/components/shared/exchange-rate-footer.js +282 -0
- package/lib/checkout-v2/components/shared/scenario-badge.d.ts +6 -0
- package/lib/checkout-v2/components/shared/scenario-badge.js +57 -0
- package/lib/checkout-v2/components/shared/total-display.d.ts +7 -0
- package/lib/checkout-v2/components/shared/total-display.js +154 -0
- package/lib/checkout-v2/index.d.ts +2 -0
- package/lib/checkout-v2/index.js +13 -0
- package/lib/checkout-v2/layouts/checkout-layout.d.ts +7 -0
- package/lib/checkout-v2/layouts/checkout-layout.js +308 -0
- package/lib/checkout-v2/panels/left/composite-panel.d.ts +1 -0
- package/lib/checkout-v2/panels/left/composite-panel.js +515 -0
- package/lib/checkout-v2/panels/left/credit-topup-panel.d.ts +1 -0
- package/lib/checkout-v2/panels/left/credit-topup-panel.js +799 -0
- package/lib/checkout-v2/panels/left/scenario-router.d.ts +1 -0
- package/lib/checkout-v2/panels/left/scenario-router.js +29 -0
- package/lib/checkout-v2/panels/right/payment-panel.d.ts +1 -0
- package/lib/checkout-v2/panels/right/payment-panel.js +906 -0
- package/lib/checkout-v2/types.d.ts +15 -0
- package/lib/checkout-v2/types.js +1 -0
- package/lib/checkout-v2/utils/format.d.ts +59 -0
- package/lib/checkout-v2/utils/format.js +158 -0
- package/lib/checkout-v2/utils/scenario-detector.d.ts +3 -0
- package/lib/checkout-v2/utils/scenario-detector.js +23 -0
- package/lib/checkout-v2/views/error-view.d.ts +7 -0
- package/lib/checkout-v2/views/error-view.js +321 -0
- package/lib/checkout-v2/views/loading-view.d.ts +5 -0
- package/lib/checkout-v2/views/loading-view.js +168 -0
- package/lib/checkout-v2/views/success-view.d.ts +29 -0
- package/lib/checkout-v2/views/success-view.js +735 -0
- package/lib/components/phone-field.d.ts +14 -0
- package/lib/components/phone-field.js +130 -0
- package/lib/index.d.ts +3 -1
- package/lib/index.js +8 -0
- package/lib/locales/en.js +45 -6
- package/lib/locales/zh.js +45 -6
- package/lib/payment/form/index.js +10 -1
- package/package.json +4 -3
- package/src/checkout-v2/checkout-v2.tsx +155 -0
- package/src/checkout-v2/components/dialogs/checkout-dialogs.tsx +134 -0
- package/src/checkout-v2/components/left/billing-toggle.tsx +122 -0
- package/src/checkout-v2/components/left/cross-sell-card.tsx +170 -0
- package/src/checkout-v2/components/left/product-item-card.tsx +634 -0
- package/src/checkout-v2/components/left/promotion-input.tsx +207 -0
- package/src/checkout-v2/components/left/staking-breakdown.tsx +57 -0
- package/src/checkout-v2/components/left/trial-info.tsx +63 -0
- package/src/checkout-v2/components/right/currency-grid.tsx +59 -0
- package/src/checkout-v2/components/right/customer-info-card.tsx +214 -0
- package/src/checkout-v2/components/right/status-feedback.tsx +35 -0
- package/src/checkout-v2/components/right/submit-button.tsx +37 -0
- package/src/checkout-v2/components/right/subscription-disclaimer.tsx +27 -0
- package/src/checkout-v2/components/shared/exchange-rate-footer.tsx +221 -0
- package/src/checkout-v2/components/shared/scenario-badge.tsx +51 -0
- package/src/checkout-v2/components/shared/total-display.tsx +112 -0
- package/src/checkout-v2/index.ts +2 -0
- package/src/checkout-v2/layouts/checkout-layout.tsx +232 -0
- package/src/checkout-v2/panels/left/composite-panel.tsx +465 -0
- package/src/checkout-v2/panels/left/credit-topup-panel.tsx +681 -0
- package/src/checkout-v2/panels/left/scenario-router.tsx +22 -0
- package/src/checkout-v2/panels/right/payment-panel.tsx +703 -0
- package/src/checkout-v2/types.ts +18 -0
- package/src/checkout-v2/utils/format.ts +204 -0
- package/src/checkout-v2/utils/scenario-detector.ts +30 -0
- package/src/checkout-v2/views/error-view.tsx +293 -0
- package/src/checkout-v2/views/loading-view.tsx +162 -0
- package/src/checkout-v2/views/success-view.tsx +770 -0
- package/src/components/phone-field.tsx +119 -0
- package/src/index.ts +3 -0
- package/src/locales/en.tsx +45 -4
- package/src/locales/zh.tsx +43 -4
- package/src/payment/form/index.tsx +16 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { useRequest } from "ahooks";
|
|
4
|
+
import { joinURL } from "ufo";
|
|
5
|
+
import { Box } from "@mui/material";
|
|
6
|
+
import { CheckoutProvider, useSessionContext, useSubmitFeature } from "@blocklet/payment-react-headless";
|
|
7
|
+
import api from "../libs/api.js";
|
|
8
|
+
import { getPrefix, mergeExtraParams } from "../libs/util.js";
|
|
9
|
+
import { PaymentThemeProvider } from "../theme/index.js";
|
|
10
|
+
import { useMobile } from "../hooks/mobile.js";
|
|
11
|
+
import CheckoutLayout from "./layouts/checkout-layout.js";
|
|
12
|
+
import ScenarioRouter from "./panels/left/scenario-router.js";
|
|
13
|
+
import PaymentPanel from "./panels/right/payment-panel.js";
|
|
14
|
+
import CheckoutDialogs from "./components/dialogs/checkout-dialogs.js";
|
|
15
|
+
import LoadingView from "./views/loading-view.js";
|
|
16
|
+
import ErrorView from "./views/error-view.js";
|
|
17
|
+
import SuccessView from "./views/success-view.js";
|
|
18
|
+
const plinkPromises = {};
|
|
19
|
+
function startFromPaymentLink(id, params) {
|
|
20
|
+
if (!plinkPromises[id]) {
|
|
21
|
+
plinkPromises[id] = api.post(`/api/checkout-sessions/start/${id}?${mergeExtraParams(params)}`).then((res) => res?.data).finally(() => {
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
delete plinkPromises[id];
|
|
24
|
+
}, 3e3);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return plinkPromises[id];
|
|
28
|
+
}
|
|
29
|
+
function CheckoutRouter({
|
|
30
|
+
onPaid = void 0,
|
|
31
|
+
onError = void 0,
|
|
32
|
+
mode = "inline"
|
|
33
|
+
}) {
|
|
34
|
+
const { isLoading, error, errorCode, session } = useSessionContext();
|
|
35
|
+
const submit = useSubmitFeature();
|
|
36
|
+
const { isMobile } = useMobile();
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (error && onError) {
|
|
39
|
+
onError(new Error(error));
|
|
40
|
+
}
|
|
41
|
+
}, [error, onError]);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (submit.status === "completed" && submit.result && onPaid) {
|
|
44
|
+
onPaid(submit.result);
|
|
45
|
+
}
|
|
46
|
+
}, [submit.status, submit.result, onPaid]);
|
|
47
|
+
if (isLoading) {
|
|
48
|
+
return /* @__PURE__ */ jsx(LoadingView, { mode });
|
|
49
|
+
}
|
|
50
|
+
if (error) {
|
|
51
|
+
return /* @__PURE__ */ jsx(ErrorView, { error, errorCode, mode });
|
|
52
|
+
}
|
|
53
|
+
const isCompleted = submit.status === "completed";
|
|
54
|
+
const mobileCompleted = isCompleted && isMobile;
|
|
55
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
56
|
+
/* @__PURE__ */ jsx(
|
|
57
|
+
CheckoutLayout,
|
|
58
|
+
{
|
|
59
|
+
left: mobileCompleted ? null : /* @__PURE__ */ jsx(
|
|
60
|
+
Box,
|
|
61
|
+
{
|
|
62
|
+
sx: {
|
|
63
|
+
flex: 1,
|
|
64
|
+
display: "flex",
|
|
65
|
+
flexDirection: "column",
|
|
66
|
+
...isCompleted ? { opacity: 0.45, pointerEvents: "none", filter: "grayscale(0.3)", transition: "all 0.5s ease" } : {}
|
|
67
|
+
},
|
|
68
|
+
children: /* @__PURE__ */ jsx(ScenarioRouter, {})
|
|
69
|
+
}
|
|
70
|
+
),
|
|
71
|
+
right: isCompleted ? /* @__PURE__ */ jsx(SuccessView, { submit, session }) : /* @__PURE__ */ jsx(PaymentPanel, {}),
|
|
72
|
+
mode
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
!isCompleted && /* @__PURE__ */ jsx(CheckoutDialogs, {})
|
|
76
|
+
] });
|
|
77
|
+
}
|
|
78
|
+
export default function CheckoutV2({
|
|
79
|
+
id,
|
|
80
|
+
onPaid,
|
|
81
|
+
onError,
|
|
82
|
+
theme = "default",
|
|
83
|
+
mode = "inline",
|
|
84
|
+
extraParams = {}
|
|
85
|
+
}) {
|
|
86
|
+
if (!id.startsWith("plink_") && !id.startsWith("cs_")) {
|
|
87
|
+
throw new Error("Either a checkoutSession or a paymentLink id is required.");
|
|
88
|
+
}
|
|
89
|
+
const isPaymentLink = id.startsWith("plink_");
|
|
90
|
+
const [resolvedSessionId, setResolvedSessionId] = useState(isPaymentLink ? null : id);
|
|
91
|
+
useRequest(
|
|
92
|
+
async () => {
|
|
93
|
+
if (!isPaymentLink) return null;
|
|
94
|
+
const data = await startFromPaymentLink(id, extraParams);
|
|
95
|
+
const csId = data?.checkoutSession?.id;
|
|
96
|
+
if (csId) {
|
|
97
|
+
setResolvedSessionId(csId);
|
|
98
|
+
if (mode === "standalone") {
|
|
99
|
+
window.history.replaceState(
|
|
100
|
+
null,
|
|
101
|
+
"",
|
|
102
|
+
joinURL(getPrefix(), `/checkout/pay/${csId}?${mergeExtraParams(extraParams)}`)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return data;
|
|
107
|
+
},
|
|
108
|
+
{ ready: isPaymentLink }
|
|
109
|
+
);
|
|
110
|
+
if (!resolvedSessionId) {
|
|
111
|
+
return /* @__PURE__ */ jsx(LoadingView, { mode });
|
|
112
|
+
}
|
|
113
|
+
const content = /* @__PURE__ */ jsx(CheckoutProvider, { sessionId: resolvedSessionId, children: /* @__PURE__ */ jsx(CheckoutRouter, { onPaid, onError, mode }) });
|
|
114
|
+
if (theme === "inherit") {
|
|
115
|
+
return content;
|
|
116
|
+
}
|
|
117
|
+
if (theme && typeof theme === "object") {
|
|
118
|
+
return /* @__PURE__ */ jsx(PaymentThemeProvider, { theme, children: content });
|
|
119
|
+
}
|
|
120
|
+
return /* @__PURE__ */ jsx(PaymentThemeProvider, { children: content });
|
|
121
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function CheckoutDialogs(): import("react").JSX.Element;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Divider, Stack, Typography } from "@mui/material";
|
|
3
|
+
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
4
|
+
import {
|
|
5
|
+
useSessionContext,
|
|
6
|
+
usePaymentMethodContext,
|
|
7
|
+
useSubmitFeature,
|
|
8
|
+
useCustomerFormFeature
|
|
9
|
+
} from "@blocklet/payment-react-headless";
|
|
10
|
+
import StripeForm from "../../../payment/form/stripe/index.js";
|
|
11
|
+
import ConfirmDialog from "../../../components/confirm.js";
|
|
12
|
+
import PriceChangeConfirm from "../../../components/price-change-confirm.js";
|
|
13
|
+
import { formatTokenAmount } from "../../utils/format.js";
|
|
14
|
+
function getRedirectUrl(session) {
|
|
15
|
+
try {
|
|
16
|
+
const params = new URLSearchParams(window.location.search);
|
|
17
|
+
const redirect = params.get("redirect");
|
|
18
|
+
if (redirect) return decodeURIComponent(redirect);
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
if (session?.success_url) return session.success_url;
|
|
22
|
+
if (session?.payment_link?.after_completion?.redirect?.url) {
|
|
23
|
+
return session.payment_link.after_completion.redirect.url;
|
|
24
|
+
}
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
export default function CheckoutDialogs() {
|
|
28
|
+
const { t } = useLocaleContext();
|
|
29
|
+
const { session } = useSessionContext();
|
|
30
|
+
const { currency, stripe } = usePaymentMethodContext();
|
|
31
|
+
const submit = useSubmitFeature();
|
|
32
|
+
const form = useCustomerFormFeature();
|
|
33
|
+
const mode = session?.mode || "payment";
|
|
34
|
+
const stripeContext = submit.context?.type === "stripe" ? submit.context : null;
|
|
35
|
+
const showStripeDialog = submit.status === "waiting_stripe" && stripeContext?.clientSecret;
|
|
36
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
37
|
+
showStripeDialog && /* @__PURE__ */ jsx(
|
|
38
|
+
StripeForm,
|
|
39
|
+
{
|
|
40
|
+
clientSecret: stripeContext.clientSecret,
|
|
41
|
+
intentType: stripeContext.intentType || "payment_intent",
|
|
42
|
+
publicKey: stripe?.publishableKey || "",
|
|
43
|
+
mode,
|
|
44
|
+
customer: {
|
|
45
|
+
name: form.values.customer_name,
|
|
46
|
+
email: form.values.customer_email,
|
|
47
|
+
phone: form.values.customer_phone,
|
|
48
|
+
address: form.values.billing_address
|
|
49
|
+
},
|
|
50
|
+
returnUrl: getRedirectUrl(session),
|
|
51
|
+
onConfirm: submit.stripeConfirm,
|
|
52
|
+
onCancel: submit.stripeCancel
|
|
53
|
+
}
|
|
54
|
+
),
|
|
55
|
+
submit.status === "confirming_price" && submit.context?.type === "price_change" && /* @__PURE__ */ jsx(
|
|
56
|
+
PriceChangeConfirm,
|
|
57
|
+
{
|
|
58
|
+
open: true,
|
|
59
|
+
changePercent: submit.context.changePercent,
|
|
60
|
+
onConfirm: submit.confirm,
|
|
61
|
+
onCancel: submit.cancel,
|
|
62
|
+
loading: false
|
|
63
|
+
}
|
|
64
|
+
),
|
|
65
|
+
submit.status === "confirming_fast_pay" && submit.context?.type === "fast_pay" && /* @__PURE__ */ jsx(
|
|
66
|
+
ConfirmDialog,
|
|
67
|
+
{
|
|
68
|
+
onConfirm: submit.confirm,
|
|
69
|
+
onCancel: submit.cancel,
|
|
70
|
+
title: submit.context.payType === "credit" ? t("payment.checkout.fastPay.credit.title") : t("payment.checkout.fastPay.title"),
|
|
71
|
+
message: submit.context.payType === "credit" ? /* @__PURE__ */ jsx(Typography, { children: t("payment.checkout.fastPay.credit.meteringSubscriptionMessage", {
|
|
72
|
+
available: `${formatTokenAmount(submit.context.amount || "0", currency)} ${currency?.symbol || ""}`
|
|
73
|
+
}) }) : /* @__PURE__ */ jsxs(Stack, { children: [
|
|
74
|
+
/* @__PURE__ */ jsx(Typography, { children: t("payment.checkout.fastPay.autoPaymentReason") }),
|
|
75
|
+
/* @__PURE__ */ jsx(Divider, { sx: { mt: 1.5, mb: 1.5 } }),
|
|
76
|
+
/* @__PURE__ */ jsxs(Stack, { spacing: 1, children: [
|
|
77
|
+
/* @__PURE__ */ jsxs(Stack, { sx: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
78
|
+
/* @__PURE__ */ jsx(Typography, { sx: { color: "text.primary", whiteSpace: "nowrap" }, children: t("payment.checkout.fastPay.payer") }),
|
|
79
|
+
/* @__PURE__ */ jsx(Typography, { sx: { color: "text.secondary", fontSize: 14 }, children: submit.context.payer ? `${submit.context.payer.slice(0, 10)}...${submit.context.payer.slice(-6)}` : "" })
|
|
80
|
+
] }),
|
|
81
|
+
/* @__PURE__ */ jsxs(Stack, { sx: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [
|
|
82
|
+
/* @__PURE__ */ jsx(Typography, { sx: { color: "text.primary" }, children: t("payment.checkout.fastPay.amount") }),
|
|
83
|
+
/* @__PURE__ */ jsxs(Typography, { children: [
|
|
84
|
+
formatTokenAmount(submit.context.amount || "0", currency),
|
|
85
|
+
" ",
|
|
86
|
+
currency?.symbol || ""
|
|
87
|
+
] })
|
|
88
|
+
] })
|
|
89
|
+
] })
|
|
90
|
+
] }),
|
|
91
|
+
loading: false,
|
|
92
|
+
color: "primary"
|
|
93
|
+
}
|
|
94
|
+
),
|
|
95
|
+
submit.status === "credit_insufficient" && submit.context?.type === "credit_insufficient" && /* @__PURE__ */ jsx(
|
|
96
|
+
ConfirmDialog,
|
|
97
|
+
{
|
|
98
|
+
onConfirm: submit.cancel,
|
|
99
|
+
onCancel: submit.cancel,
|
|
100
|
+
title: t("payment.checkout.fastPay.credit.insufficientTitle"),
|
|
101
|
+
message: /* @__PURE__ */ jsx(Typography, { children: t("payment.checkout.fastPay.credit.insufficientMessage") }),
|
|
102
|
+
confirm: t("common.confirm")
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
] });
|
|
106
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BillingIntervalData } from '@blocklet/payment-react-headless';
|
|
2
|
+
interface BillingToggleProps {
|
|
3
|
+
billingInterval: BillingIntervalData | null;
|
|
4
|
+
}
|
|
5
|
+
export default function BillingToggle({ billingInterval }: BillingToggleProps): import("react").JSX.Element | null;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import TrendingDownIcon from "@mui/icons-material/TrendingDown";
|
|
3
|
+
import { Box, CircularProgress, Stack, Typography } from "@mui/material";
|
|
4
|
+
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
5
|
+
import { INTERVAL_LOCALE_KEY } from "../../utils/format.js";
|
|
6
|
+
export default function BillingToggle({ billingInterval }) {
|
|
7
|
+
const { t } = useLocaleContext();
|
|
8
|
+
if (!billingInterval?.available?.length || billingInterval.available.length <= 1) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const best = billingInterval.available.reduce(
|
|
12
|
+
(a, b) => Number(b.savings || 0) > Number(a.savings || 0) ? b : a
|
|
13
|
+
);
|
|
14
|
+
return /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", spacing: 2, sx: { mb: 3 }, children: [
|
|
15
|
+
/* @__PURE__ */ jsx(
|
|
16
|
+
Stack,
|
|
17
|
+
{
|
|
18
|
+
direction: "row",
|
|
19
|
+
alignItems: "center",
|
|
20
|
+
sx: {
|
|
21
|
+
bgcolor: "background.paper",
|
|
22
|
+
borderRadius: "9999px",
|
|
23
|
+
border: "1px solid",
|
|
24
|
+
borderColor: "divider",
|
|
25
|
+
p: "4px",
|
|
26
|
+
display: "inline-flex",
|
|
27
|
+
boxShadow: (theme) => theme.palette.mode === "dark" ? "0 1px 2px 0 rgba(0,0,0,0.3)" : "0 1px 2px 0 rgba(0,0,0,0.05)"
|
|
28
|
+
},
|
|
29
|
+
children: billingInterval.available.map((option) => {
|
|
30
|
+
const isSelected = option.interval === billingInterval.current;
|
|
31
|
+
const selectedSx = {
|
|
32
|
+
bgcolor: (th) => th.palette.mode === "dark" ? "rgba(156,106,222,0.25)" : "rgba(156,106,222,0.15)",
|
|
33
|
+
color: "primary.main",
|
|
34
|
+
backdropFilter: "blur(8px)",
|
|
35
|
+
WebkitBackdropFilter: "blur(8px)",
|
|
36
|
+
boxShadow: (th) => th.palette.mode === "dark" ? "0 2px 8px rgba(156,106,222,0.2), inset 0 1px 0 rgba(255,255,255,0.06)" : "0 2px 8px rgba(156,106,222,0.15), inset 0 1px 0 rgba(255,255,255,0.5)",
|
|
37
|
+
border: "1px solid",
|
|
38
|
+
borderColor: (th) => th.palette.mode === "dark" ? "rgba(156,106,222,0.3)" : "rgba(156,106,222,0.2)"
|
|
39
|
+
};
|
|
40
|
+
const unselectedSx = {
|
|
41
|
+
color: "text.secondary",
|
|
42
|
+
border: "1px solid transparent",
|
|
43
|
+
"&:hover": { color: "text.primary" }
|
|
44
|
+
};
|
|
45
|
+
return /* @__PURE__ */ jsx(
|
|
46
|
+
Box,
|
|
47
|
+
{
|
|
48
|
+
onClick: () => {
|
|
49
|
+
if (option.interval !== billingInterval.current && !billingInterval.switching) {
|
|
50
|
+
billingInterval.switch(option.interval);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
sx: {
|
|
54
|
+
px: 3.5,
|
|
55
|
+
py: 1,
|
|
56
|
+
borderRadius: "9999px",
|
|
57
|
+
cursor: "pointer",
|
|
58
|
+
transition: "all 0.3s ease",
|
|
59
|
+
userSelect: "none",
|
|
60
|
+
...isSelected ? selectedSx : unselectedSx
|
|
61
|
+
},
|
|
62
|
+
children: billingInterval.switching && isSelected ? /* @__PURE__ */ jsx(CircularProgress, { size: 14, color: "inherit", sx: { my: "1px" } }) : /* @__PURE__ */ jsx(
|
|
63
|
+
Typography,
|
|
64
|
+
{
|
|
65
|
+
component: "span",
|
|
66
|
+
sx: {
|
|
67
|
+
fontSize: 14,
|
|
68
|
+
fontWeight: 700,
|
|
69
|
+
color: "inherit",
|
|
70
|
+
lineHeight: 1
|
|
71
|
+
},
|
|
72
|
+
children: t(INTERVAL_LOCALE_KEY[option.interval] || option.interval)
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
},
|
|
76
|
+
option.interval
|
|
77
|
+
);
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
),
|
|
81
|
+
best.savings && Number(best.savings) > 0 && /* @__PURE__ */ jsxs(
|
|
82
|
+
Stack,
|
|
83
|
+
{
|
|
84
|
+
direction: "row",
|
|
85
|
+
alignItems: "center",
|
|
86
|
+
spacing: 0.75,
|
|
87
|
+
sx: {
|
|
88
|
+
px: 1.5,
|
|
89
|
+
py: 0.75,
|
|
90
|
+
bgcolor: (theme) => theme.palette.mode === "dark" ? "rgba(18,184,134,0.1)" : "#ebfef5",
|
|
91
|
+
color: "#12b886",
|
|
92
|
+
fontSize: 11,
|
|
93
|
+
fontWeight: 700,
|
|
94
|
+
borderRadius: "9999px",
|
|
95
|
+
border: "1px solid",
|
|
96
|
+
borderColor: (theme) => theme.palette.mode === "dark" ? "rgba(18,184,134,0.2)" : "#d3f9e8",
|
|
97
|
+
textTransform: "uppercase",
|
|
98
|
+
letterSpacing: "0.05em"
|
|
99
|
+
},
|
|
100
|
+
children: [
|
|
101
|
+
/* @__PURE__ */ jsx(TrendingDownIcon, { sx: { fontSize: 14 } }),
|
|
102
|
+
/* @__PURE__ */ jsxs(
|
|
103
|
+
Typography,
|
|
104
|
+
{
|
|
105
|
+
component: "span",
|
|
106
|
+
sx: { fontSize: "inherit", fontWeight: "inherit", color: "inherit", lineHeight: 1 },
|
|
107
|
+
children: [
|
|
108
|
+
"SAVE ",
|
|
109
|
+
best.savings,
|
|
110
|
+
"%"
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
] });
|
|
118
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TPaymentCurrency, TPrice } from '@blocklet/payment-types';
|
|
2
|
+
interface CrossSellCardProps {
|
|
3
|
+
crossSellItem: TPrice;
|
|
4
|
+
currency: TPaymentCurrency | null;
|
|
5
|
+
exchangeRate: string | null;
|
|
6
|
+
crossSellRequired: boolean;
|
|
7
|
+
onAdd: () => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export default function CrossSellCard({ crossSellItem, currency, exchangeRate, crossSellRequired, onAdd, }: CrossSellCardProps): import("react").JSX.Element | null;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import AddShoppingCartIcon from "@mui/icons-material/AddShoppingCart";
|
|
3
|
+
import ShoppingCartCheckoutIcon from "@mui/icons-material/ShoppingCartCheckout";
|
|
4
|
+
import { Avatar, Box, Button, Chip, Stack, Typography } from "@mui/material";
|
|
5
|
+
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
6
|
+
import { formatDynamicUnitPrice, tSafe, INTERVAL_LOCALE_KEY } from "../../utils/format.js";
|
|
7
|
+
export default function CrossSellCard({
|
|
8
|
+
crossSellItem,
|
|
9
|
+
currency,
|
|
10
|
+
exchangeRate,
|
|
11
|
+
crossSellRequired,
|
|
12
|
+
onAdd
|
|
13
|
+
}) {
|
|
14
|
+
const { t } = useLocaleContext();
|
|
15
|
+
if (!crossSellItem) return null;
|
|
16
|
+
const { product } = crossSellItem;
|
|
17
|
+
const productImage = product?.images?.[0] || "";
|
|
18
|
+
const productName = product?.name || t("payment.checkout.cross_sell.add");
|
|
19
|
+
const { recurring } = crossSellItem;
|
|
20
|
+
const intervalKey = recurring?.interval ? INTERVAL_LOCALE_KEY[recurring.interval] : null;
|
|
21
|
+
const rawDescription = product?.description || "";
|
|
22
|
+
const subtitle = (() => {
|
|
23
|
+
if (rawDescription && rawDescription !== productName) return rawDescription;
|
|
24
|
+
return intervalKey ? t(intervalKey) : "";
|
|
25
|
+
})();
|
|
26
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { position: "relative" }, children: [
|
|
27
|
+
crossSellRequired && /* @__PURE__ */ jsx(
|
|
28
|
+
Chip,
|
|
29
|
+
{
|
|
30
|
+
label: tSafe(t, "payment.checkout.cross_sell.recommended", "RECOMMENDED"),
|
|
31
|
+
size: "small",
|
|
32
|
+
sx: {
|
|
33
|
+
position: "absolute",
|
|
34
|
+
top: 0,
|
|
35
|
+
right: 40,
|
|
36
|
+
transform: "translateY(-50%)",
|
|
37
|
+
zIndex: 1,
|
|
38
|
+
height: 22,
|
|
39
|
+
fontSize: 9,
|
|
40
|
+
fontWeight: 900,
|
|
41
|
+
letterSpacing: "0.12em",
|
|
42
|
+
bgcolor: "primary.main",
|
|
43
|
+
color: "#fff",
|
|
44
|
+
boxShadow: "0 4px 12px rgba(45,124,243,0.2)",
|
|
45
|
+
"& .MuiChip-label": { px: 1.5 }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
),
|
|
49
|
+
/* @__PURE__ */ jsx(
|
|
50
|
+
Box,
|
|
51
|
+
{
|
|
52
|
+
sx: {
|
|
53
|
+
p: { xs: 2, md: 3 },
|
|
54
|
+
bgcolor: "background.paper",
|
|
55
|
+
borderRadius: { xs: "16px", md: "24px" },
|
|
56
|
+
border: "2px dashed",
|
|
57
|
+
borderColor: (theme) => theme.palette.mode === "dark" ? "rgba(255,255,255,0.15)" : "rgba(45,124,243,0.25)",
|
|
58
|
+
boxShadow: (theme) => theme.palette.mode === "dark" ? "0 12px 40px -8px rgba(0,0,0,0.3)" : "0 12px 40px -8px rgba(0,0,0,0.06)",
|
|
59
|
+
transition: "all 0.3s ease",
|
|
60
|
+
cursor: "pointer",
|
|
61
|
+
"&:hover": {
|
|
62
|
+
borderColor: "primary.main",
|
|
63
|
+
boxShadow: "0 12px 40px -8px rgba(0,0,0,0.1)"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
onClick: onAdd,
|
|
67
|
+
children: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: { xs: 1.5, md: 2.5 }, sx: { alignItems: "center", width: "100%" }, children: [
|
|
68
|
+
productImage ? /* @__PURE__ */ jsx(
|
|
69
|
+
Avatar,
|
|
70
|
+
{
|
|
71
|
+
src: productImage,
|
|
72
|
+
variant: "rounded",
|
|
73
|
+
sx: {
|
|
74
|
+
width: { xs: 44, md: 64 },
|
|
75
|
+
height: { xs: 44, md: 64 },
|
|
76
|
+
borderRadius: { xs: "12px", md: "16px" },
|
|
77
|
+
flexShrink: 0
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
) : /* @__PURE__ */ jsx(
|
|
81
|
+
Avatar,
|
|
82
|
+
{
|
|
83
|
+
variant: "rounded",
|
|
84
|
+
sx: {
|
|
85
|
+
width: { xs: 44, md: 64 },
|
|
86
|
+
height: { xs: 44, md: 64 },
|
|
87
|
+
borderRadius: { xs: "12px", md: "16px" },
|
|
88
|
+
bgcolor: (theme) => theme.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "#eff6ff",
|
|
89
|
+
flexShrink: 0
|
|
90
|
+
},
|
|
91
|
+
children: /* @__PURE__ */ jsx(ShoppingCartCheckoutIcon, { sx: { fontSize: { xs: 22, md: 28 }, color: "primary.main", opacity: 0.45 } })
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
/* @__PURE__ */ jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [
|
|
95
|
+
/* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "flex-start", sx: { mb: 0.25 }, children: [
|
|
96
|
+
/* @__PURE__ */ jsx(
|
|
97
|
+
Typography,
|
|
98
|
+
{
|
|
99
|
+
sx: { fontWeight: 800, fontSize: { xs: 15, md: 18 }, color: "text.primary", lineHeight: 1.3 },
|
|
100
|
+
children: productName
|
|
101
|
+
}
|
|
102
|
+
),
|
|
103
|
+
/* @__PURE__ */ jsxs(Stack, { alignItems: "flex-end", sx: { flexShrink: 0, ml: { xs: 1, md: 2 } }, children: [
|
|
104
|
+
/* @__PURE__ */ jsxs(
|
|
105
|
+
Typography,
|
|
106
|
+
{
|
|
107
|
+
sx: { fontWeight: 800, color: "text.primary", whiteSpace: "nowrap", fontSize: { xs: 15, md: 18 } },
|
|
108
|
+
children: [
|
|
109
|
+
formatDynamicUnitPrice(crossSellItem, currency, exchangeRate),
|
|
110
|
+
" ",
|
|
111
|
+
currency?.symbol
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
),
|
|
115
|
+
exchangeRate && crossSellItem.base_amount && /* @__PURE__ */ jsxs(Typography, { sx: { fontSize: 10, color: "text.disabled", fontWeight: 700, lineHeight: 1 }, children: [
|
|
116
|
+
"\u2248 $",
|
|
117
|
+
Number(crossSellItem.base_amount || 0).toFixed(2)
|
|
118
|
+
] })
|
|
119
|
+
] })
|
|
120
|
+
] }),
|
|
121
|
+
subtitle && /* @__PURE__ */ jsx(
|
|
122
|
+
Typography,
|
|
123
|
+
{
|
|
124
|
+
sx: { fontSize: { xs: 12, md: 14 }, color: "text.secondary", fontWeight: 500, lineHeight: 1.4 },
|
|
125
|
+
noWrap: true,
|
|
126
|
+
children: subtitle
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
/* @__PURE__ */ jsx(Box, { sx: { pt: { xs: 1.5, md: 2 } }, children: /* @__PURE__ */ jsx(
|
|
130
|
+
Button,
|
|
131
|
+
{
|
|
132
|
+
size: "small",
|
|
133
|
+
variant: "outlined",
|
|
134
|
+
startIcon: /* @__PURE__ */ jsx(AddShoppingCartIcon, { sx: { fontSize: "14px !important" } }),
|
|
135
|
+
sx: {
|
|
136
|
+
textTransform: "none",
|
|
137
|
+
fontSize: { xs: 11, md: 12 },
|
|
138
|
+
fontWeight: 700,
|
|
139
|
+
borderRadius: "8px",
|
|
140
|
+
border: "1px solid",
|
|
141
|
+
borderColor: "divider",
|
|
142
|
+
color: "primary.main",
|
|
143
|
+
bgcolor: "background.paper",
|
|
144
|
+
boxShadow: 1,
|
|
145
|
+
px: 1.5,
|
|
146
|
+
py: 0.5,
|
|
147
|
+
transition: "all 0.2s",
|
|
148
|
+
"&:hover": {
|
|
149
|
+
bgcolor: "primary.main",
|
|
150
|
+
color: "#fff",
|
|
151
|
+
borderColor: "primary.main"
|
|
152
|
+
},
|
|
153
|
+
"&:active": { transform: "scale(0.95)" }
|
|
154
|
+
},
|
|
155
|
+
onClick: (e) => {
|
|
156
|
+
e.stopPropagation();
|
|
157
|
+
onAdd();
|
|
158
|
+
},
|
|
159
|
+
children: tSafe(t, "payment.checkout.cross_sell.addToOrder", "Add to order")
|
|
160
|
+
}
|
|
161
|
+
) })
|
|
162
|
+
] })
|
|
163
|
+
] })
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
|
|
2
|
+
interface ProductItemCardProps {
|
|
3
|
+
item: TLineItemExpanded & {
|
|
4
|
+
adjustable_quantity?: {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
minimum?: number;
|
|
7
|
+
maximum?: number;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
currency: TPaymentCurrency | null;
|
|
11
|
+
discounts: any[];
|
|
12
|
+
exchangeRate: string | null;
|
|
13
|
+
onQuantityChange: (itemId: string, qty: number) => Promise<void>;
|
|
14
|
+
onUpsell: (fromId: string, toId: string) => Promise<void>;
|
|
15
|
+
onDownsell: (priceId: string) => Promise<void>;
|
|
16
|
+
trialActive: boolean;
|
|
17
|
+
trialDays: number;
|
|
18
|
+
t: (key: string, params?: any) => string;
|
|
19
|
+
recommended?: boolean;
|
|
20
|
+
hideUpsell?: boolean;
|
|
21
|
+
isRateLoading?: boolean;
|
|
22
|
+
showFeatures?: boolean;
|
|
23
|
+
children?: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
export default function ProductItemCard({ item, currency, discounts, exchangeRate, onQuantityChange, onUpsell, onDownsell, trialActive, trialDays, t, recommended, hideUpsell, isRateLoading, showFeatures, children, }: ProductItemCardProps): import("react").JSX.Element;
|
|
26
|
+
export {};
|