@blocklet/payment-react 1.13.113 → 1.13.115

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/README.md CHANGED
@@ -1,18 +1,59 @@
1
1
  # Payment Kit Components
2
2
 
3
3
  ```tsx
4
- import { PaymentProvider, Checkout, PricingTable } from '@blocklet/payment-react';
4
+ import { CheckoutForm, CheckoutTable, PaymentProvider } from '@blocklet/payment-react';
5
+ import { Paper, Typography } from '@mui/material';
6
+ import React from 'react';
7
+ import { useSearchParams } from 'react-router-dom';
5
8
 
6
- export default function Payment() {
9
+ import { useSessionContext } from '../contexts/session';
10
+
11
+ export default function CheckoutPage() {
12
+ const [params] = useSearchParams();
7
13
  const { session, connectApi } = useSessionContext();
8
- const { locale } = useLocaleContext();
9
14
 
10
- return (<PaymentProvider session={session} connect={connectApi} locale={locale}>
11
- <Checkout checkoutSessionId="" onComplete={noop} onError={noop} params={{}} />
12
- <Checkout paymentLinkId="" onComplete={noop} onError={noop} params={{}} />
13
- <Checkout pricingTableId="" onSelect={noop} onComplete={noop} onError={noop} params={{}} />
14
- </PaymentProvider>);
15
+ if (params.get('type') === 'session') {
16
+ return (
17
+ <PaymentProvider session={session} connect={connectApi}>
18
+ <Typography variant="h4" gutterBottom>
19
+ Checkout with session
20
+ </Typography>
21
+ <Paper sx={{ p: 3, display: 'inline-block' }} elevation={3}>
22
+ <CheckoutForm id="cs_9zeD2yCgPXT9Vit9bab2vVC3V7DFjHiKvyoLzVTVzAf4XSU2oLWY67vKy7" mode="inline" />
23
+ </Paper>
24
+ </PaymentProvider>
25
+ );
26
+ }
27
+
28
+ if (params.get('type') === 'link') {
29
+ return (
30
+ <PaymentProvider session={session} connect={connectApi}>
31
+ <Typography variant="h4" gutterBottom>
32
+ Checkout with payment link
33
+ </Typography>
34
+ <Paper sx={{ p: 3, display: 'inline-block' }} elevation={3}>
35
+ <CheckoutForm id="plink_oB1I6FNeHKSkuq81fhJy0vIZ" mode="inline" />
36
+ </Paper>
37
+ </PaymentProvider>
38
+ );
39
+ }
40
+
41
+ if (params.get('type') === 'table') {
42
+ return (
43
+ <PaymentProvider session={session} connect={connectApi}>
44
+ <Typography variant="h4" gutterBottom>
45
+ Checkout with pricing table
46
+ </Typography>
47
+ <Paper sx={{ p: 3, display: 'inline-block' }} elevation={3}>
48
+ <CheckoutTable id="prctbl_kOsaIiPrsHAwwALaKgy17mIl" mode="inline" />
49
+ </Paper>
50
+ </PaymentProvider>
51
+ );
52
+ }
53
+
54
+ return null;
15
55
  }
56
+
16
57
  ```
17
58
 
18
59
  ## I18n
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import { CheckoutProps } from '../types';
3
- declare function Checkout({ id, onPaid, onError, mode, extraParams }: CheckoutProps): import("react").JSX.Element;
4
- declare namespace Checkout {
3
+ declare function CheckoutForm({ id, onPaid, onError, mode, extraParams }: CheckoutProps): import("react").JSX.Element;
4
+ declare namespace CheckoutForm {
5
5
  var defaultProps: {
6
6
  onPaid: any;
7
7
  onError: {
@@ -12,4 +12,4 @@ declare namespace Checkout {
12
12
  extraParams: {};
13
13
  };
14
14
  }
15
- export default Checkout;
15
+ export default CheckoutForm;
@@ -14,9 +14,9 @@ const fetchCheckoutSession = async (id) => {
14
14
  const { data } = await api.get(`/api/checkout-sessions/retrieve/${id}`);
15
15
  return data;
16
16
  };
17
- export default function Checkout({ id, onPaid, onError, mode, extraParams }) {
17
+ export default function CheckoutForm({ id, onPaid, onError, mode, extraParams }) {
18
18
  if (!id.startsWith("plink_") && !id.startsWith("cs_")) {
19
- throw new Error("Either a checkout session or a payment link id is required.");
19
+ throw new Error("Either a checkoutSession or a paymentLink id is required.");
20
20
  }
21
21
  const type = id.startsWith("plink_") ? "paymentLink" : "checkoutSession";
22
22
  const [state, setState] = useSetState({ completed: false, appError: null });
@@ -53,7 +53,7 @@ export default function Checkout({ id, onPaid, onError, mode, extraParams }) {
53
53
  }
54
54
  );
55
55
  }
56
- Checkout.defaultProps = {
56
+ CheckoutForm.defaultProps = {
57
57
  onPaid: noop,
58
58
  onError: console.error,
59
59
  mode: "inline",
@@ -0,0 +1,3 @@
1
+ /// <reference types="react" />
2
+ import { CheckoutProps } from '../types';
3
+ export default function CheckoutTable({ id, onPaid, onError, mode, extraParams }: CheckoutProps): import("react").JSX.Element;
@@ -0,0 +1,71 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
3
+ import Toast from "@arcblock/ux/lib/Toast";
4
+ import { Alert, Stack, Typography } from "@mui/material";
5
+ import { useRequest } from "ahooks";
6
+ import { useState } from "react";
7
+ import api from "../api.js";
8
+ import Livemode from "../components/livemode.js";
9
+ import PricingTable from "../components/pricing-table.js";
10
+ import ProductSkeleton from "../payment/product-skeleton.js";
11
+ import { mergeExtraParams } from "../util.js";
12
+ import CheckoutForm from "./form.js";
13
+ const fetchData = async (id) => {
14
+ const { data } = await api.get(`/api/pricing-tables/${id}`);
15
+ return data;
16
+ };
17
+ export default function CheckoutTable({ id, onPaid, onError, mode, extraParams }) {
18
+ if (!id.startsWith("prctbl_")) {
19
+ throw new Error("A valid pricing table id is required.");
20
+ }
21
+ const { t } = useLocaleContext();
22
+ const [sessionId, setSessionId] = useState("");
23
+ const { error, loading, data } = useRequest(() => fetchData(id));
24
+ if (error) {
25
+ return /* @__PURE__ */ jsx(Alert, { severity: "error", children: error.message });
26
+ }
27
+ if (loading || !data) {
28
+ return /* @__PURE__ */ jsxs(
29
+ Stack,
30
+ {
31
+ flexWrap: "wrap",
32
+ direction: "row",
33
+ gap: { xs: 3, sm: 5, md: mode === "checkout" ? 10 : 5 },
34
+ justifyContent: "center",
35
+ children: [
36
+ /* @__PURE__ */ jsx(ProductSkeleton, { count: 2 }, 1),
37
+ /* @__PURE__ */ jsx(ProductSkeleton, { count: 3 }, 2),
38
+ /* @__PURE__ */ jsx(ProductSkeleton, { count: 4 }, 3)
39
+ ]
40
+ }
41
+ );
42
+ }
43
+ if (data.items.length === 0) {
44
+ return /* @__PURE__ */ jsx(Alert, { severity: "warning", children: t("payment.checkout.noPricing") });
45
+ }
46
+ const handleSelect = (priceId) => {
47
+ api.post(`/api/pricing-tables/${data.id}/checkout/${priceId}?${mergeExtraParams(extraParams)}`).then((res) => {
48
+ if (mode === "standalone") {
49
+ window.location.replace(res.data.url);
50
+ } else {
51
+ setSessionId(res.data.id);
52
+ }
53
+ }).catch((err) => {
54
+ console.error(err);
55
+ Toast.error(err.message);
56
+ });
57
+ };
58
+ if (!sessionId) {
59
+ if (mode === "standalone") {
60
+ return /* @__PURE__ */ jsxs(Stack, { direction: "column", alignItems: "center", spacing: 4, children: [
61
+ /* @__PURE__ */ jsxs(Typography, { variant: "h4", color: "text.primary", fontWeight: 600, children: [
62
+ data.name,
63
+ !data.livemode && /* @__PURE__ */ jsx(Livemode, {})
64
+ ] }),
65
+ /* @__PURE__ */ jsx(PricingTable, { table: data, onSelect: handleSelect })
66
+ ] });
67
+ }
68
+ return /* @__PURE__ */ jsx(PricingTable, { mode: "select", table: data, onSelect: handleSelect });
69
+ }
70
+ return /* @__PURE__ */ jsx(CheckoutForm, { id: sessionId, onPaid, onError, mode });
71
+ }
@@ -59,8 +59,6 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
59
59
  } catch (err) {
60
60
  console.error(err);
61
61
  Toast.error(formatError(err));
62
- } finally {
63
- setState({ loading: "" });
64
62
  }
65
63
  };
66
64
  return /* @__PURE__ */ jsxs(
package/es/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import api from './api';
2
- import Checkout from './checkout';
2
+ import CheckoutForm from './checkout/form';
3
+ import CheckoutTable from './checkout/table';
3
4
  import FormInput from './components/input';
4
5
  import Livemode from './components/livemode';
5
6
  import PricingTable from './components/pricing-table';
@@ -13,4 +14,4 @@ import ProductSkeleton from './payment/product-skeleton';
13
14
  export * from './util';
14
15
  export * from './contexts/payment';
15
16
  export { translations, createTranslator } from './locales';
16
- export { api, dayjs, FormInput, PhoneInput, Status, Livemode, Switch, Checkout, Payment, PricingTable, ProductSkeleton, Amount, };
17
+ export { api, dayjs, FormInput, PhoneInput, Status, Livemode, Switch, CheckoutForm, CheckoutTable, Payment, PricingTable, ProductSkeleton, Amount, };
package/es/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import api from "./api.js";
2
- import Checkout from "./checkout/index.js";
2
+ import CheckoutForm from "./checkout/form.js";
3
+ import CheckoutTable from "./checkout/table.js";
3
4
  import FormInput from "./components/input.js";
4
5
  import Livemode from "./components/livemode.js";
5
6
  import PricingTable from "./components/pricing-table.js";
@@ -21,7 +22,8 @@ export {
21
22
  Status,
22
23
  Livemode,
23
24
  Switch,
24
- Checkout,
25
+ CheckoutForm,
26
+ CheckoutTable,
25
27
  Payment,
26
28
  PricingTable,
27
29
  ProductSkeleton,
@@ -9,6 +9,6 @@ export default function UserButtons() {
9
9
  const { session } = usePaymentContext();
10
10
  return /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", children: [
11
11
  /* @__PURE__ */ jsx(LocaleSelector, { showText: false }),
12
- session.user ? /* @__PURE__ */ jsx(SessionManager, { session }) : /* @__PURE__ */ jsx(Tooltip, { title: t("payment.checkout.login"), arrow: true, children: /* @__PURE__ */ jsx(SessionManager, { session }) })
12
+ session?.user ? /* @__PURE__ */ jsx(SessionManager, { session }) : /* @__PURE__ */ jsx(Tooltip, { title: t("payment.checkout.login"), arrow: true, children: /* @__PURE__ */ jsx(SessionManager, { session }) })
13
13
  ] });
14
14
  }
@@ -60,7 +60,7 @@ export default function PaymentForm({
60
60
  stripePaying: false
61
61
  });
62
62
  useEffect(() => {
63
- if (session.user) {
63
+ if (session?.user) {
64
64
  const values = getValues();
65
65
  if (!values.customer_name) {
66
66
  setValue("customer_name", session.user.fullName);
@@ -72,7 +72,7 @@ export default function PaymentForm({
72
72
  setValue("customer_phone", session.user.phone);
73
73
  }
74
74
  }
75
- }, [session.user, getValues, setValue]);
75
+ }, [session?.user, getValues, setValue]);
76
76
  const paymentMethod = useWatch({ control, name: "payment_method" });
77
77
  const paymentCurrency = useWatch({ control, name: "payment_currency" });
78
78
  const paymentCurrencies = paymentMethods.find((x) => x.id === paymentMethod)?.payment_currencies || [];
@@ -86,7 +86,7 @@ export default function PaymentForm({
86
86
  return false;
87
87
  }, [domSize, theme]);
88
88
  const payee = getStatementDescriptor(checkoutSession.line_items);
89
- const buttonText = session.user ? t(`payment.checkout.${checkoutSession.mode}`) : t("payment.checkout.connect", { action: t(`payment.checkout.${checkoutSession.mode}`) });
89
+ const buttonText = session?.user ? t(`payment.checkout.${checkoutSession.mode}`) : t("payment.checkout.connect", { action: t(`payment.checkout.${checkoutSession.mode}`) });
90
90
  const method = paymentMethods.find((x) => x.id === paymentMethod);
91
91
  const handleMethodChange = (e) => {
92
92
  setValue("payment_method", e.target.value);
@@ -190,10 +190,10 @@ export default function PaymentForm({
190
190
  }
191
191
  };
192
192
  const onAction = () => {
193
- if (session.user) {
193
+ if (session?.user) {
194
194
  handleSubmit(onSubmit)();
195
195
  } else {
196
- session.login({
196
+ session?.login({
197
197
  onSuccess: onUserLoggedIn,
198
198
  extraParams: {}
199
199
  });
@@ -1,4 +1,5 @@
1
1
  /// <reference types="react" />
2
+ import { LiteralUnion } from 'type-fest';
2
3
  import { CheckoutCallbacks, CheckoutContext } from '../types';
3
4
  type Props = CheckoutContext & CheckoutCallbacks & {
4
5
  completed?: boolean;
@@ -24,5 +25,5 @@ export declare namespace PaymentInner {
24
25
  export declare const Root: import("@emotion/styled").StyledComponent<import("@mui/system").BoxOwnProps<import("@mui/material").Theme> & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
25
26
  ref?: ((instance: HTMLDivElement | null) => void) | import("react").RefObject<HTMLDivElement> | null | undefined;
26
27
  }, keyof import("@mui/system").BoxOwnProps<import("@mui/material").Theme>> & import("@mui/system").MUIStyledCommonProps<import("@mui/system").Theme> & {
27
- mode: string;
28
+ mode: LiteralUnion<'standalone' | 'inline' | 'popup', string>;
28
29
  }, {}, {}>;
@@ -109,9 +109,9 @@ export function PaymentInner({
109
109
  const defaultMethodId = paymentMethods.find((m) => m.payment_currencies.some((c) => c.id === defaultCurrencyId))?.id;
110
110
  const methods = useForm({
111
111
  defaultValues: {
112
- customer_name: customer?.name || session.user?.fullName || "",
113
- customer_email: customer?.email || session.user?.email || "",
114
- customer_phone: customer?.phone || session.user?.phone || "",
112
+ customer_name: customer?.name || session?.user?.fullName || "",
113
+ customer_email: customer?.email || session?.user?.email || "",
114
+ customer_phone: customer?.phone || session?.user?.phone || "",
115
115
  payment_method: defaultMethodId,
116
116
  payment_currency: defaultCurrencyId,
117
117
  billing_address: Object.assign(
@@ -166,9 +166,9 @@ export function PaymentInner({
166
166
  Toast.error(formatError(err));
167
167
  }
168
168
  };
169
- return /* @__PURE__ */ jsx(FormProvider, { ...methods, children: /* @__PURE__ */ jsx(Root, { mode, children: /* @__PURE__ */ jsxs(Stack, { className: "cko-container", children: [
169
+ return /* @__PURE__ */ jsx(FormProvider, { ...methods, children: /* @__PURE__ */ jsx(Root, { mode, children: /* @__PURE__ */ jsxs(Stack, { className: "cko-container", sx: { gap: { sm: mode === "standalone" ? 0 : 8 } }, children: [
170
170
  /* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(Stack, { className: "cko-overview", direction: "column", children: [
171
- /* @__PURE__ */ jsx(PaymentHeader, { checkoutSession: state.checkoutSession }),
171
+ mode === "standalone" ? /* @__PURE__ */ jsx(PaymentHeader, { checkoutSession: state.checkoutSession }) : null,
172
172
  /* @__PURE__ */ jsx(
173
173
  PaymentSummary,
174
174
  {
@@ -203,7 +203,7 @@ export function PaymentInner({
203
203
  }
204
204
  )
205
205
  ] }),
206
- /* @__PURE__ */ jsx(CheckoutFooter, { className: "cko-footer" })
206
+ mode === "standalone" && /* @__PURE__ */ jsx(CheckoutFooter, { className: "cko-footer" })
207
207
  ] }) }) });
208
208
  }
209
209
  export const Root = styled(Box)`
@@ -212,10 +212,10 @@ export const Root = styled(Box)`
212
212
  flex-direction: column;
213
213
  justify-content: center;
214
214
  align-items: center;
215
- min-height: 100vh;
215
+ min-height: ${(props) => props.mode === "standalone" ? "100vh" : "auto"};
216
216
  position: relative;
217
217
 
218
- &:before {
218
+ ${(props) => props.mode === "standalone" ? `&:before {
219
219
  animation-fill-mode: both;
220
220
  background: #ffffff;
221
221
  content: '';
@@ -226,7 +226,7 @@ export const Root = styled(Box)`
226
226
  transform-origin: right;
227
227
  width: 50%;
228
228
  box-shadow: 15px 0 30px 0 rgba(0, 0, 0, 0.18);
229
- }
229
+ }` : ""}
230
230
 
231
231
  .cko-container {
232
232
  width: 100%;
@@ -235,12 +235,12 @@ export const Root = styled(Box)`
235
235
  flex-direction: row;
236
236
  justify-content: space-between;
237
237
  position: relative;
238
- padding: 0 16px;
238
+ padding: ${(props) => props.mode === "standalone" ? "0 16px" : "0"};
239
239
  }
240
240
 
241
241
  .cko-overview {
242
242
  width: 400px;
243
- min-height: 540px;
243
+ min-height: ${(props) => props.mode === "standalone" ? "540px" : "auto"};
244
244
  position: relative;
245
245
  }
246
246
  .cko-header {
@@ -251,6 +251,9 @@ export const Root = styled(Box)`
251
251
  top: 0;
252
252
  transition: background-color 0.15s ease, box-shadow 0.15s ease-out;
253
253
  }
254
+ .cko-product {
255
+ margin-top: ${(props) => props.mode === "standalone" ? "64px" : "0"};
256
+ }
254
257
  .cko-product-summary {
255
258
  }
256
259
 
@@ -90,89 +90,79 @@ export default function PaymentSummary({
90
90
  setState({ loading: false });
91
91
  }
92
92
  };
93
- return /* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(
94
- Stack,
95
- {
96
- className: "cko-product",
97
- direction: "column",
98
- sx: {
99
- mt: 8
100
- },
101
- children: [
102
- /* @__PURE__ */ jsxs(Stack, { className: "cko-product-summary", direction: "column", alignItems: "flex-start", sx: { mb: 4 }, children: [
103
- /* @__PURE__ */ jsx(Typography, { sx: { fontWeight: 500, fontSize: "1.15rem", color: "text.secondary" }, children: headlines.action }),
104
- /* @__PURE__ */ jsx(PaymentAmount, { amount: headlines.amount }),
105
- headlines.then && /* @__PURE__ */ jsx(Typography, { sx: { fontSize: "0.9rem", color: "text.secondary" }, children: headlines.then })
106
- ] }),
107
- /* @__PURE__ */ jsx(Stack, { spacing: 2, children: checkoutSession.line_items.map((x) => /* @__PURE__ */ jsx(
108
- ProductItem,
93
+ return /* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(Stack, { className: "cko-product", direction: "column", children: [
94
+ /* @__PURE__ */ jsxs(Stack, { className: "cko-product-summary", direction: "column", alignItems: "flex-start", sx: { mb: 4 }, children: [
95
+ /* @__PURE__ */ jsx(Typography, { sx: { fontWeight: 500, fontSize: "1.15rem", color: "text.secondary" }, children: headlines.action }),
96
+ /* @__PURE__ */ jsx(PaymentAmount, { amount: headlines.amount }),
97
+ headlines.then && /* @__PURE__ */ jsx(Typography, { sx: { fontSize: "0.9rem", color: "text.secondary" }, children: headlines.then })
98
+ ] }),
99
+ /* @__PURE__ */ jsx(Stack, { spacing: 2, children: checkoutSession.line_items.map((x) => /* @__PURE__ */ jsx(
100
+ ProductItem,
101
+ {
102
+ item: x,
103
+ session: checkoutSession,
104
+ currency,
105
+ onUpsell: handleUpsell,
106
+ onDownsell: handleDownsell,
107
+ children: x.cross_sell && /* @__PURE__ */ jsx(
108
+ LoadingButton,
109
109
  {
110
- item: x,
111
- session: checkoutSession,
112
- currency,
113
- onUpsell: handleUpsell,
114
- onDownsell: handleDownsell,
115
- children: x.cross_sell && /* @__PURE__ */ jsx(
110
+ size: "small",
111
+ loadingPosition: "end",
112
+ color: "error",
113
+ variant: "text",
114
+ loading: state.loading,
115
+ onClick: handleCancelCrossSell,
116
+ children: t("payment.checkout.cross_sell.remove")
117
+ }
118
+ )
119
+ },
120
+ x.price_id
121
+ )) }),
122
+ data && checkoutSession.line_items.some((x) => x.price_id === data.id) === false && /* @__PURE__ */ jsx(Grow, { in: true, children: /* @__PURE__ */ jsxs(
123
+ Stack,
124
+ {
125
+ direction: "column",
126
+ alignItems: "flex-end",
127
+ spacing: 0.5,
128
+ sx: {
129
+ border: "1px solid #eee",
130
+ borderRadius: 1,
131
+ padding: 1,
132
+ animation: state.shake ? `${shake} 0.2s 5 ease-in-out` : "none",
133
+ mt: {
134
+ xs: 4,
135
+ md: 8
136
+ }
137
+ },
138
+ children: [
139
+ /* @__PURE__ */ jsx(
140
+ ProductItem,
141
+ {
142
+ item: { quantity: 1, price: data, price_id: data.id, cross_sell: true },
143
+ session: checkoutSession,
144
+ currency,
145
+ onUpsell: noop,
146
+ onDownsell: noop
147
+ }
148
+ ),
149
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", sx: { width: 1 }, children: [
150
+ /* @__PURE__ */ jsx(Typography, { children: checkoutSession.cross_sell_behavior === "required" && /* @__PURE__ */ jsx(Status, { label: t("payment.checkout.required"), color: "info", variant: "outlined", sx: { mr: 1 } }) }),
151
+ /* @__PURE__ */ jsx(
116
152
  LoadingButton,
117
153
  {
118
154
  size: "small",
119
155
  loadingPosition: "end",
120
- color: "error",
121
- variant: "text",
156
+ color: checkoutSession.cross_sell_behavior === "required" ? "info" : "info",
157
+ variant: checkoutSession.cross_sell_behavior === "required" ? "text" : "text",
122
158
  loading: state.loading,
123
- onClick: handleCancelCrossSell,
124
- children: t("payment.checkout.cross_sell.remove")
159
+ onClick: handleApplyCrossSell,
160
+ children: t("payment.checkout.cross_sell.add")
125
161
  }
126
162
  )
127
- },
128
- x.price_id
129
- )) }),
130
- data && checkoutSession.line_items.some((x) => x.price_id === data.id) === false && /* @__PURE__ */ jsx(Grow, { in: true, children: /* @__PURE__ */ jsxs(
131
- Stack,
132
- {
133
- direction: "column",
134
- alignItems: "flex-end",
135
- spacing: 0.5,
136
- sx: {
137
- border: "1px solid #eee",
138
- borderRadius: 1,
139
- padding: 1,
140
- animation: state.shake ? `${shake} 0.2s 5 ease-in-out` : "none",
141
- mt: {
142
- xs: 4,
143
- md: 8
144
- }
145
- },
146
- children: [
147
- /* @__PURE__ */ jsx(
148
- ProductItem,
149
- {
150
- item: { quantity: 1, price: data, price_id: data.id, cross_sell: true },
151
- session: checkoutSession,
152
- currency,
153
- onUpsell: noop,
154
- onDownsell: noop
155
- }
156
- ),
157
- /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", sx: { width: 1 }, children: [
158
- /* @__PURE__ */ jsx(Typography, { children: checkoutSession.cross_sell_behavior === "required" && /* @__PURE__ */ jsx(Status, { label: t("payment.checkout.required"), color: "info", variant: "outlined", sx: { mr: 1 } }) }),
159
- /* @__PURE__ */ jsx(
160
- LoadingButton,
161
- {
162
- size: "small",
163
- loadingPosition: "end",
164
- color: checkoutSession.cross_sell_behavior === "required" ? "info" : "info",
165
- variant: checkoutSession.cross_sell_behavior === "required" ? "text" : "text",
166
- loading: state.loading,
167
- onClick: handleApplyCrossSell,
168
- children: t("payment.checkout.cross_sell.add")
169
- }
170
- )
171
- ] })
172
- ]
173
- }
174
- ) })
175
- ]
176
- }
177
- ) });
163
+ ] })
164
+ ]
165
+ }
166
+ ) })
167
+ ] }) });
178
168
  }
@@ -1,7 +1,7 @@
1
1
  /// <reference types="react" />
2
2
  import { CheckoutProps } from '../types';
3
- declare function Checkout({ id, onPaid, onError, mode, extraParams }: CheckoutProps): import("react").JSX.Element;
4
- declare namespace Checkout {
3
+ declare function CheckoutForm({ id, onPaid, onError, mode, extraParams }: CheckoutProps): import("react").JSX.Element;
4
+ declare namespace CheckoutForm {
5
5
  var defaultProps: {
6
6
  onPaid: any;
7
7
  onError: {
@@ -12,4 +12,4 @@ declare namespace Checkout {
12
12
  extraParams: {};
13
13
  };
14
14
  }
15
- export default Checkout;
15
+ export default CheckoutForm;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- module.exports = Checkout;
6
+ module.exports = CheckoutForm;
7
7
  var _jsxRuntime = require("react/jsx-runtime");
8
8
  var _ahooks = require("ahooks");
9
9
  var _noop = _interopRequireDefault(require("lodash/noop"));
@@ -25,7 +25,7 @@ const fetchCheckoutSession = async id => {
25
25
  } = await _api.default.get(`/api/checkout-sessions/retrieve/${id}`);
26
26
  return data;
27
27
  };
28
- function Checkout({
28
+ function CheckoutForm({
29
29
  id,
30
30
  onPaid,
31
31
  onError,
@@ -33,7 +33,7 @@ function Checkout({
33
33
  extraParams
34
34
  }) {
35
35
  if (!id.startsWith("plink_") && !id.startsWith("cs_")) {
36
- throw new Error("Either a checkout session or a payment link id is required.");
36
+ throw new Error("Either a checkoutSession or a paymentLink id is required.");
37
37
  }
38
38
  const type = id.startsWith("plink_") ? "paymentLink" : "checkoutSession";
39
39
  const [state, setState] = (0, _ahooks.useSetState)({
@@ -75,7 +75,7 @@ function Checkout({
75
75
  mode
76
76
  });
77
77
  }
78
- Checkout.defaultProps = {
78
+ CheckoutForm.defaultProps = {
79
79
  onPaid: _noop.default,
80
80
  onError: console.error,
81
81
  mode: "inline",
@@ -0,0 +1,3 @@
1
+ /// <reference types="react" />
2
+ import { CheckoutProps } from '../types';
3
+ export default function CheckoutTable({ id, onPaid, onError, mode, extraParams }: CheckoutProps): import("react").JSX.Element;
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ module.exports = CheckoutTable;
7
+ var _jsxRuntime = require("react/jsx-runtime");
8
+ var _context = require("@arcblock/ux/lib/Locale/context");
9
+ var _Toast = _interopRequireDefault(require("@arcblock/ux/lib/Toast"));
10
+ var _material = require("@mui/material");
11
+ var _ahooks = require("ahooks");
12
+ var _react = require("react");
13
+ var _api = _interopRequireDefault(require("../api"));
14
+ var _livemode = _interopRequireDefault(require("../components/livemode"));
15
+ var _pricingTable = _interopRequireDefault(require("../components/pricing-table"));
16
+ var _productSkeleton = _interopRequireDefault(require("../payment/product-skeleton"));
17
+ var _util = require("../util");
18
+ var _form = _interopRequireDefault(require("./form"));
19
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20
+ const fetchData = async id => {
21
+ const {
22
+ data
23
+ } = await _api.default.get(`/api/pricing-tables/${id}`);
24
+ return data;
25
+ };
26
+ function CheckoutTable({
27
+ id,
28
+ onPaid,
29
+ onError,
30
+ mode,
31
+ extraParams
32
+ }) {
33
+ if (!id.startsWith("prctbl_")) {
34
+ throw new Error("A valid pricing table id is required.");
35
+ }
36
+ const {
37
+ t
38
+ } = (0, _context.useLocaleContext)();
39
+ const [sessionId, setSessionId] = (0, _react.useState)("");
40
+ const {
41
+ error,
42
+ loading,
43
+ data
44
+ } = (0, _ahooks.useRequest)(() => fetchData(id));
45
+ if (error) {
46
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Alert, {
47
+ severity: "error",
48
+ children: error.message
49
+ });
50
+ }
51
+ if (loading || !data) {
52
+ return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
53
+ flexWrap: "wrap",
54
+ direction: "row",
55
+ gap: {
56
+ xs: 3,
57
+ sm: 5,
58
+ md: mode === "checkout" ? 10 : 5
59
+ },
60
+ justifyContent: "center",
61
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_productSkeleton.default, {
62
+ count: 2
63
+ }, 1), /* @__PURE__ */(0, _jsxRuntime.jsx)(_productSkeleton.default, {
64
+ count: 3
65
+ }, 2), /* @__PURE__ */(0, _jsxRuntime.jsx)(_productSkeleton.default, {
66
+ count: 4
67
+ }, 3)]
68
+ });
69
+ }
70
+ if (data.items.length === 0) {
71
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Alert, {
72
+ severity: "warning",
73
+ children: t("payment.checkout.noPricing")
74
+ });
75
+ }
76
+ const handleSelect = priceId => {
77
+ _api.default.post(`/api/pricing-tables/${data.id}/checkout/${priceId}?${(0, _util.mergeExtraParams)(extraParams)}`).then(res => {
78
+ if (mode === "standalone") {
79
+ window.location.replace(res.data.url);
80
+ } else {
81
+ setSessionId(res.data.id);
82
+ }
83
+ }).catch(err => {
84
+ console.error(err);
85
+ _Toast.default.error(err.message);
86
+ });
87
+ };
88
+ if (!sessionId) {
89
+ if (mode === "standalone") {
90
+ return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
91
+ direction: "column",
92
+ alignItems: "center",
93
+ spacing: 4,
94
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
95
+ variant: "h4",
96
+ color: "text.primary",
97
+ fontWeight: 600,
98
+ children: [data.name, !data.livemode && /* @__PURE__ */(0, _jsxRuntime.jsx)(_livemode.default, {})]
99
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_pricingTable.default, {
100
+ table: data,
101
+ onSelect: handleSelect
102
+ })]
103
+ });
104
+ }
105
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_pricingTable.default, {
106
+ mode: "select",
107
+ table: data,
108
+ onSelect: handleSelect
109
+ });
110
+ }
111
+ return /* @__PURE__ */(0, _jsxRuntime.jsx)(_form.default, {
112
+ id: sessionId,
113
+ onPaid,
114
+ onError,
115
+ mode
116
+ });
117
+ }
@@ -76,10 +76,6 @@ function PricingTable({
76
76
  } catch (err) {
77
77
  console.error(err);
78
78
  _Toast.default.error((0, _util.formatError)(err));
79
- } finally {
80
- setState({
81
- loading: ""
82
- });
83
79
  }
84
80
  };
85
81
  return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
package/lib/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import api from './api';
2
- import Checkout from './checkout';
2
+ import CheckoutForm from './checkout/form';
3
+ import CheckoutTable from './checkout/table';
3
4
  import FormInput from './components/input';
4
5
  import Livemode from './components/livemode';
5
6
  import PricingTable from './components/pricing-table';
@@ -13,4 +14,4 @@ import ProductSkeleton from './payment/product-skeleton';
13
14
  export * from './util';
14
15
  export * from './contexts/payment';
15
16
  export { translations, createTranslator } from './locales';
16
- export { api, dayjs, FormInput, PhoneInput, Status, Livemode, Switch, Checkout, Payment, PricingTable, ProductSkeleton, Amount, };
17
+ export { api, dayjs, FormInput, PhoneInput, Status, Livemode, Switch, CheckoutForm, CheckoutTable, Payment, PricingTable, ProductSkeleton, Amount, };
package/lib/index.js CHANGED
@@ -5,7 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  var _exportNames = {
7
7
  api: true,
8
- Checkout: true,
8
+ CheckoutForm: true,
9
+ CheckoutTable: true,
9
10
  FormInput: true,
10
11
  Livemode: true,
11
12
  PricingTable: true,
@@ -25,10 +26,16 @@ Object.defineProperty(exports, "Amount", {
25
26
  return _amount.default;
26
27
  }
27
28
  });
28
- Object.defineProperty(exports, "Checkout", {
29
+ Object.defineProperty(exports, "CheckoutForm", {
29
30
  enumerable: true,
30
31
  get: function () {
31
- return _checkout.default;
32
+ return _form.default;
33
+ }
34
+ });
35
+ Object.defineProperty(exports, "CheckoutTable", {
36
+ enumerable: true,
37
+ get: function () {
38
+ return _table.default;
32
39
  }
33
40
  });
34
41
  Object.defineProperty(exports, "FormInput", {
@@ -104,7 +111,8 @@ Object.defineProperty(exports, "translations", {
104
111
  }
105
112
  });
106
113
  var _api = _interopRequireDefault(require("./api"));
107
- var _checkout = _interopRequireDefault(require("./checkout"));
114
+ var _form = _interopRequireDefault(require("./checkout/form"));
115
+ var _table = _interopRequireDefault(require("./checkout/table"));
108
116
  var _input = _interopRequireDefault(require("./components/input"));
109
117
  var _livemode = _interopRequireDefault(require("./components/livemode"));
110
118
  var _pricingTable = _interopRequireDefault(require("./components/pricing-table"));
@@ -24,7 +24,7 @@ function UserButtons() {
24
24
  justifyContent: "space-between",
25
25
  children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_selector.default, {
26
26
  showText: false
27
- }), session.user ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_SessionManager.default, {
27
+ }), session?.user ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_SessionManager.default, {
28
28
  session
29
29
  }) : /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Tooltip, {
30
30
  title: t("payment.checkout.login"),
@@ -79,7 +79,7 @@ function PaymentForm({
79
79
  stripePaying: false
80
80
  });
81
81
  (0, _react.useEffect)(() => {
82
- if (session.user) {
82
+ if (session?.user) {
83
83
  const values = getValues();
84
84
  if (!values.customer_name) {
85
85
  setValue("customer_name", session.user.fullName);
@@ -91,7 +91,7 @@ function PaymentForm({
91
91
  setValue("customer_phone", session.user.phone);
92
92
  }
93
93
  }
94
- }, [session.user, getValues, setValue]);
94
+ }, [session?.user, getValues, setValue]);
95
95
  const paymentMethod = (0, _reactHookForm.useWatch)({
96
96
  control,
97
97
  name: "payment_method"
@@ -111,7 +111,7 @@ function PaymentForm({
111
111
  return false;
112
112
  }, [domSize, theme]);
113
113
  const payee = (0, _util.getStatementDescriptor)(checkoutSession.line_items);
114
- const buttonText = session.user ? t(`payment.checkout.${checkoutSession.mode}`) : t("payment.checkout.connect", {
114
+ const buttonText = session?.user ? t(`payment.checkout.${checkoutSession.mode}`) : t("payment.checkout.connect", {
115
115
  action: t(`payment.checkout.${checkoutSession.mode}`)
116
116
  });
117
117
  const method = paymentMethods.find(x => x.id === paymentMethod);
@@ -242,10 +242,10 @@ function PaymentForm({
242
242
  }
243
243
  };
244
244
  const onAction = () => {
245
- if (session.user) {
245
+ if (session?.user) {
246
246
  handleSubmit(onSubmit)();
247
247
  } else {
248
- session.login({
248
+ session?.login({
249
249
  onSuccess: onUserLoggedIn,
250
250
  extraParams: {}
251
251
  });
@@ -1,4 +1,5 @@
1
1
  /// <reference types="react" />
2
+ import { LiteralUnion } from 'type-fest';
2
3
  import { CheckoutCallbacks, CheckoutContext } from '../types';
3
4
  type Props = CheckoutContext & CheckoutCallbacks & {
4
5
  completed?: boolean;
@@ -24,5 +25,5 @@ export declare namespace PaymentInner {
24
25
  export declare const Root: import("@emotion/styled").StyledComponent<import("@mui/system").BoxOwnProps<import("@mui/material").Theme> & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
25
26
  ref?: ((instance: HTMLDivElement | null) => void) | import("react").RefObject<HTMLDivElement> | null | undefined;
26
27
  }, keyof import("@mui/system").BoxOwnProps<import("@mui/material").Theme>> & import("@mui/system").MUIStyledCommonProps<import("@mui/system").Theme> & {
27
- mode: string;
28
+ mode: LiteralUnion<'standalone' | 'inline' | 'popup', string>;
28
29
  }, {}, {}>;
@@ -135,9 +135,9 @@ function PaymentInner({
135
135
  const defaultMethodId = paymentMethods.find(m => m.payment_currencies.some(c => c.id === defaultCurrencyId))?.id;
136
136
  const methods = (0, _reactHookForm.useForm)({
137
137
  defaultValues: {
138
- customer_name: customer?.name || session.user?.fullName || "",
139
- customer_email: customer?.email || session.user?.email || "",
140
- customer_phone: customer?.phone || session.user?.phone || "",
138
+ customer_name: customer?.name || session?.user?.fullName || "",
139
+ customer_email: customer?.email || session?.user?.email || "",
140
+ customer_phone: customer?.phone || session?.user?.phone || "",
141
141
  payment_method: defaultMethodId,
142
142
  payment_currency: defaultCurrencyId,
143
143
  billing_address: Object.assign({
@@ -219,14 +219,19 @@ function PaymentInner({
219
219
  mode,
220
220
  children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
221
221
  className: "cko-container",
222
+ sx: {
223
+ gap: {
224
+ sm: mode === "standalone" ? 0 : 8
225
+ }
226
+ },
222
227
  children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Fade, {
223
228
  in: true,
224
229
  children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
225
230
  className: "cko-overview",
226
231
  direction: "column",
227
- children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_header.default, {
232
+ children: [mode === "standalone" ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_header.default, {
228
233
  checkoutSession: state.checkoutSession
229
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_summary.default, {
234
+ }) : null, /* @__PURE__ */(0, _jsxRuntime.jsx)(_summary.default, {
230
235
  checkoutSession: state.checkoutSession,
231
236
  currency,
232
237
  onUpsell,
@@ -252,7 +257,7 @@ function PaymentInner({
252
257
  onError,
253
258
  mode
254
259
  })]
255
- }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_footer.default, {
260
+ }), mode === "standalone" && /* @__PURE__ */(0, _jsxRuntime.jsx)(_footer.default, {
256
261
  className: "cko-footer"
257
262
  })]
258
263
  })
@@ -265,10 +270,10 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
265
270
  flex-direction: column;
266
271
  justify-content: center;
267
272
  align-items: center;
268
- min-height: 100vh;
273
+ min-height: ${props => props.mode === "standalone" ? "100vh" : "auto"};
269
274
  position: relative;
270
275
 
271
- &:before {
276
+ ${props => props.mode === "standalone" ? `&:before {
272
277
  animation-fill-mode: both;
273
278
  background: #ffffff;
274
279
  content: '';
@@ -279,7 +284,7 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
279
284
  transform-origin: right;
280
285
  width: 50%;
281
286
  box-shadow: 15px 0 30px 0 rgba(0, 0, 0, 0.18);
282
- }
287
+ }` : ""}
283
288
 
284
289
  .cko-container {
285
290
  width: 100%;
@@ -288,12 +293,12 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
288
293
  flex-direction: row;
289
294
  justify-content: space-between;
290
295
  position: relative;
291
- padding: 0 16px;
296
+ padding: ${props => props.mode === "standalone" ? "0 16px" : "0"};
292
297
  }
293
298
 
294
299
  .cko-overview {
295
300
  width: 400px;
296
- min-height: 540px;
301
+ min-height: ${props => props.mode === "standalone" ? "540px" : "auto"};
297
302
  position: relative;
298
303
  }
299
304
  .cko-header {
@@ -304,6 +309,9 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
304
309
  top: 0;
305
310
  transition: background-color 0.15s ease, box-shadow 0.15s ease-out;
306
311
  }
312
+ .cko-product {
313
+ margin-top: ${props => props.mode === "standalone" ? "64px" : "0"};
314
+ }
307
315
  .cko-product-summary {
308
316
  }
309
317
 
@@ -121,9 +121,6 @@ function PaymentSummary({
121
121
  children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
122
122
  className: "cko-product",
123
123
  direction: "column",
124
- sx: {
125
- mt: 8
126
- },
127
124
  children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
128
125
  className: "cko-product-summary",
129
126
  direction: "column",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react",
3
- "version": "1.13.113",
3
+ "version": "1.13.115",
4
4
  "description": "Reusable react components for payment kit v2",
5
5
  "keywords": [
6
6
  "react",
@@ -86,7 +86,7 @@
86
86
  "@babel/core": "^7.19.3",
87
87
  "@babel/preset-env": "^7.19.3",
88
88
  "@babel/preset-react": "^7.18.6",
89
- "@blocklet/payment-types": "1.13.113",
89
+ "@blocklet/payment-types": "1.13.115",
90
90
  "@types/react": "^18.2.42",
91
91
  "@types/react-dom": "^18.2.17",
92
92
  "eslint": "^8.56.0",
@@ -100,5 +100,5 @@
100
100
  "typescript": "^4.9.5",
101
101
  "unbuild": "^2.0.0"
102
102
  },
103
- "gitHead": "d1a99d9bbdb06d4e7742d7741ec511fa0fee4663"
103
+ "gitHead": "60a62f67098162dcba38edb26b6efbacdb98dd00"
104
104
  }
@@ -20,9 +20,10 @@ const fetchCheckoutSession = async (id: string): Promise<CheckoutContext> => {
20
20
  return data;
21
21
  };
22
22
 
23
- export default function Checkout({ id, onPaid, onError, mode, extraParams }: CheckoutProps) {
23
+ // FIXME: @wangshijun support popup
24
+ export default function CheckoutForm({ id, onPaid, onError, mode, extraParams }: CheckoutProps) {
24
25
  if (!id.startsWith('plink_') && !id.startsWith('cs_')) {
25
- throw new Error('Either a checkout session or a payment link id is required.');
26
+ throw new Error('Either a checkoutSession or a paymentLink id is required.');
26
27
  }
27
28
 
28
29
  const type = id.startsWith('plink_') ? 'paymentLink' : 'checkoutSession';
@@ -66,7 +67,7 @@ export default function Checkout({ id, onPaid, onError, mode, extraParams }: Che
66
67
  );
67
68
  }
68
69
 
69
- Checkout.defaultProps = {
70
+ CheckoutForm.defaultProps = {
70
71
  onPaid: noop,
71
72
  onError: console.error,
72
73
  mode: 'inline',
@@ -0,0 +1,85 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import Toast from '@arcblock/ux/lib/Toast';
3
+ import type { TPricingTableExpanded } from '@blocklet/payment-types';
4
+ import { Alert, Stack, Typography } from '@mui/material';
5
+ import { useRequest } from 'ahooks';
6
+ import { useState } from 'react';
7
+
8
+ import api from '../api';
9
+ import Livemode from '../components/livemode';
10
+ import PricingTable from '../components/pricing-table';
11
+ import ProductSkeleton from '../payment/product-skeleton';
12
+ import { CheckoutProps } from '../types';
13
+ import { mergeExtraParams } from '../util';
14
+ import CheckoutForm from './form';
15
+
16
+ const fetchData = async (id: string): Promise<TPricingTableExpanded> => {
17
+ const { data } = await api.get(`/api/pricing-tables/${id}`);
18
+ return data;
19
+ };
20
+
21
+ export default function CheckoutTable({ id, onPaid, onError, mode, extraParams }: CheckoutProps) {
22
+ if (!id.startsWith('prctbl_')) {
23
+ throw new Error('A valid pricing table id is required.');
24
+ }
25
+
26
+ const { t } = useLocaleContext();
27
+ const [sessionId, setSessionId] = useState('');
28
+ const { error, loading, data } = useRequest(() => fetchData(id));
29
+
30
+ if (error) {
31
+ return <Alert severity="error">{error.message}</Alert>;
32
+ }
33
+
34
+ if (loading || !data) {
35
+ return (
36
+ <Stack
37
+ flexWrap="wrap"
38
+ direction="row"
39
+ gap={{ xs: 3, sm: 5, md: mode === 'checkout' ? 10 : 5 }}
40
+ justifyContent="center">
41
+ <ProductSkeleton key={1} count={2} />
42
+ <ProductSkeleton key={2} count={3} />
43
+ <ProductSkeleton key={3} count={4} />
44
+ </Stack>
45
+ );
46
+ }
47
+
48
+ if (data.items.length === 0) {
49
+ return <Alert severity="warning">{t('payment.checkout.noPricing')}</Alert>;
50
+ }
51
+
52
+ const handleSelect = (priceId: string) => {
53
+ api
54
+ .post(`/api/pricing-tables/${data.id}/checkout/${priceId}?${mergeExtraParams(extraParams)}`)
55
+ .then((res) => {
56
+ if (mode === 'standalone') {
57
+ window.location.replace(res.data.url);
58
+ } else {
59
+ setSessionId(res.data.id);
60
+ }
61
+ })
62
+ .catch((err) => {
63
+ console.error(err);
64
+ Toast.error(err.message);
65
+ });
66
+ };
67
+
68
+ if (!sessionId) {
69
+ if (mode === 'standalone') {
70
+ return (
71
+ <Stack direction="column" alignItems="center" spacing={4}>
72
+ <Typography variant="h4" color="text.primary" fontWeight={600}>
73
+ {data.name}
74
+ {!data.livemode && <Livemode />}
75
+ </Typography>
76
+ <PricingTable table={data} onSelect={handleSelect} />
77
+ </Stack>
78
+ );
79
+ }
80
+
81
+ return <PricingTable mode="select" table={data} onSelect={handleSelect} />;
82
+ }
83
+
84
+ return <CheckoutForm id={sessionId} onPaid={onPaid} onError={onError} mode={mode} />;
85
+ }
@@ -80,8 +80,6 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
80
80
  } catch (err) {
81
81
  console.error(err);
82
82
  Toast.error(formatError(err));
83
- } finally {
84
- setState({ loading: '' });
85
83
  }
86
84
  };
87
85
 
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import api from './api';
2
- import Checkout from './checkout';
2
+ import CheckoutForm from './checkout/form';
3
+ import CheckoutTable from './checkout/table';
3
4
  import FormInput from './components/input';
4
5
  import Livemode from './components/livemode';
5
6
  import PricingTable from './components/pricing-table';
@@ -24,7 +25,8 @@ export {
24
25
  Status,
25
26
  Livemode,
26
27
  Switch,
27
- Checkout,
28
+ CheckoutForm,
29
+ CheckoutTable,
28
30
  Payment,
29
31
  PricingTable,
30
32
  ProductSkeleton,
@@ -12,7 +12,7 @@ export default function UserButtons() {
12
12
  return (
13
13
  <Stack direction="row" alignItems="center" justifyContent="space-between">
14
14
  <LocaleSelector showText={false} />
15
- {session.user ? (
15
+ {session?.user ? (
16
16
  <SessionManager session={session} />
17
17
  ) : (
18
18
  <Tooltip title={t('payment.checkout.login')} arrow>
@@ -99,7 +99,7 @@ export default function PaymentForm({
99
99
  });
100
100
 
101
101
  useEffect(() => {
102
- if (session.user) {
102
+ if (session?.user) {
103
103
  const values = getValues();
104
104
  if (!values.customer_name) {
105
105
  setValue('customer_name', session.user.fullName);
@@ -111,7 +111,7 @@ export default function PaymentForm({
111
111
  setValue('customer_phone', session.user.phone);
112
112
  }
113
113
  }
114
- }, [session.user, getValues, setValue]);
114
+ }, [session?.user, getValues, setValue]);
115
115
 
116
116
  const paymentMethod = useWatch({ control, name: 'payment_method' });
117
117
  const paymentCurrency = useWatch({ control, name: 'payment_currency' });
@@ -129,7 +129,7 @@ export default function PaymentForm({
129
129
  }, [domSize, theme]);
130
130
 
131
131
  const payee = getStatementDescriptor(checkoutSession.line_items);
132
- const buttonText = session.user
132
+ const buttonText = session?.user
133
133
  ? t(`payment.checkout.${checkoutSession.mode}`)
134
134
  : t('payment.checkout.connect', { action: t(`payment.checkout.${checkoutSession.mode}`) });
135
135
 
@@ -243,10 +243,10 @@ export default function PaymentForm({
243
243
  };
244
244
 
245
245
  const onAction = () => {
246
- if (session.user) {
246
+ if (session?.user) {
247
247
  handleSubmit(onSubmit)();
248
248
  } else {
249
- session.login({
249
+ session?.login({
250
250
  onSuccess: onUserLoggedIn,
251
251
  extraParams: {},
252
252
  });
@@ -1,3 +1,4 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
1
2
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
3
  import Toast from '@arcblock/ux/lib/Toast';
3
4
  import type { TCustomer, TPaymentCurrency, TPaymentMethodExpanded } from '@blocklet/payment-types';
@@ -6,6 +7,7 @@ import { styled } from '@mui/system';
6
7
  import { useSetState } from 'ahooks';
7
8
  import { useEffect } from 'react';
8
9
  import { FormProvider, useForm } from 'react-hook-form';
10
+ import { LiteralUnion } from 'type-fest';
9
11
 
10
12
  import api from '../api';
11
13
  import { usePaymentContext } from '../contexts/payment';
@@ -134,9 +136,9 @@ export function PaymentInner({
134
136
 
135
137
  const methods = useForm({
136
138
  defaultValues: {
137
- customer_name: customer?.name || session.user?.fullName || '',
138
- customer_email: customer?.email || session.user?.email || '',
139
- customer_phone: customer?.phone || session.user?.phone || '',
139
+ customer_name: customer?.name || session?.user?.fullName || '',
140
+ customer_email: customer?.email || session?.user?.email || '',
141
+ customer_phone: customer?.phone || session?.user?.phone || '',
140
142
  payment_method: defaultMethodId,
141
143
  payment_currency: defaultCurrencyId,
142
144
  billing_address: Object.assign(
@@ -202,10 +204,10 @@ export function PaymentInner({
202
204
  return (
203
205
  <FormProvider {...methods}>
204
206
  <Root mode={mode}>
205
- <Stack className="cko-container">
207
+ <Stack className="cko-container" sx={{ gap: { sm: mode === 'standalone' ? 0 : 8 } }}>
206
208
  <Fade in>
207
209
  <Stack className="cko-overview" direction="column">
208
- <PaymentHeader checkoutSession={state.checkoutSession} />
210
+ {mode === 'standalone' ? <PaymentHeader checkoutSession={state.checkoutSession} /> : null}
209
211
  <PaymentSummary
210
212
  checkoutSession={state.checkoutSession}
211
213
  currency={currency}
@@ -239,23 +241,25 @@ export function PaymentInner({
239
241
  />
240
242
  )}
241
243
  </Stack>
242
- <CheckoutFooter className="cko-footer" />
244
+ {mode === 'standalone' && <CheckoutFooter className="cko-footer" />}
243
245
  </Stack>
244
246
  </Root>
245
247
  </FormProvider>
246
248
  );
247
249
  }
248
250
 
249
- export const Root = styled(Box)<{ mode: string }>`
251
+ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | 'popup', string> }>`
250
252
  box-sizing: border-box;
251
253
  display: flex;
252
254
  flex-direction: column;
253
255
  justify-content: center;
254
256
  align-items: center;
255
- min-height: 100vh;
257
+ min-height: ${(props) => (props.mode === 'standalone' ? '100vh' : 'auto')};
256
258
  position: relative;
257
259
 
258
- &:before {
260
+ ${(props) =>
261
+ props.mode === 'standalone'
262
+ ? `&:before {
259
263
  animation-fill-mode: both;
260
264
  background: #ffffff;
261
265
  content: '';
@@ -266,7 +270,8 @@ export const Root = styled(Box)<{ mode: string }>`
266
270
  transform-origin: right;
267
271
  width: 50%;
268
272
  box-shadow: 15px 0 30px 0 rgba(0, 0, 0, 0.18);
269
- }
273
+ }`
274
+ : ''}
270
275
 
271
276
  .cko-container {
272
277
  width: 100%;
@@ -275,12 +280,12 @@ export const Root = styled(Box)<{ mode: string }>`
275
280
  flex-direction: row;
276
281
  justify-content: space-between;
277
282
  position: relative;
278
- padding: 0 16px;
283
+ padding: ${(props) => (props.mode === 'standalone' ? '0 16px' : '0')};
279
284
  }
280
285
 
281
286
  .cko-overview {
282
287
  width: 400px;
283
- min-height: 540px;
288
+ min-height: ${(props) => (props.mode === 'standalone' ? '540px' : 'auto')};
284
289
  position: relative;
285
290
  }
286
291
  .cko-header {
@@ -291,6 +296,9 @@ export const Root = styled(Box)<{ mode: string }>`
291
296
  top: 0;
292
297
  transition: background-color 0.15s ease, box-shadow 0.15s ease-out;
293
298
  }
299
+ .cko-product {
300
+ margin-top: ${(props) => (props.mode === 'standalone' ? '64px' : '0')};
301
+ }
294
302
  .cko-product-summary {
295
303
  }
296
304
 
@@ -112,12 +112,7 @@ export default function PaymentSummary({
112
112
 
113
113
  return (
114
114
  <Fade in>
115
- <Stack
116
- className="cko-product"
117
- direction="column"
118
- sx={{
119
- mt: 8,
120
- }}>
115
+ <Stack className="cko-product" direction="column">
121
116
  <Stack className="cko-product-summary" direction="column" alignItems="flex-start" sx={{ mb: 4 }}>
122
117
  <Typography sx={{ fontWeight: 500, fontSize: '1.15rem', color: 'text.secondary' }}>
123
118
  {headlines.action}