@blocklet/payment-react 1.13.282 → 1.13.284
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/table.js +4 -2
- package/es/components/pricing-table.d.ts +1 -1
- package/es/components/pricing-table.js +94 -45
- package/es/libs/util.d.ts +1 -0
- package/es/libs/util.js +11 -5
- package/es/payment/form/index.js +6 -2
- package/lib/checkout/table.js +6 -2
- package/lib/components/pricing-table.d.ts +1 -1
- package/lib/components/pricing-table.js +94 -35
- package/lib/libs/util.d.ts +1 -0
- package/lib/libs/util.js +13 -3
- package/lib/payment/form/index.js +5 -1
- package/package.json +3 -3
- package/src/checkout/table.tsx +4 -2
- package/src/components/pricing-table.tsx +99 -46
- package/src/libs/util.ts +14 -5
- package/src/payment/form/index.tsx +7 -2
package/es/checkout/table.js
CHANGED
|
@@ -31,10 +31,12 @@ export default function CheckoutTable({ id, mode, onPaid, onError, onChange, ext
|
|
|
31
31
|
if (data.items.length === 0) {
|
|
32
32
|
return /* @__PURE__ */ jsx(Alert, { severity: "warning", children: t("payment.checkout.noPricing") });
|
|
33
33
|
}
|
|
34
|
-
const handleSelect = (priceId) => {
|
|
34
|
+
const handleSelect = (priceId, currencyId) => {
|
|
35
35
|
api.post(`/api/pricing-tables/${data.id}/checkout/${priceId}?${mergeExtraParams(extraParams)}`).then((res) => {
|
|
36
36
|
if (mode === "standalone") {
|
|
37
|
-
|
|
37
|
+
let { url } = res.data;
|
|
38
|
+
url = url.indexOf("?") > -1 ? `${url}¤cyId=${currencyId}` : `${url}?currencyId=${currencyId}`;
|
|
39
|
+
window.location.replace(url);
|
|
38
40
|
} else {
|
|
39
41
|
window.location.hash = res.data.id;
|
|
40
42
|
setSessionId(res.data.id);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { TPricingTableExpanded } from '@blocklet/payment-types';
|
|
3
3
|
type Props = {
|
|
4
4
|
table: TPricingTableExpanded;
|
|
5
|
-
onSelect: (priceId: string) => void;
|
|
5
|
+
onSelect: (priceId: string, currencyId: string) => void;
|
|
6
6
|
alignItems?: 'center' | 'left';
|
|
7
7
|
mode?: 'checkout' | 'select';
|
|
8
8
|
interval?: string;
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
ListItem,
|
|
11
11
|
ListItemIcon,
|
|
12
12
|
ListItemText,
|
|
13
|
+
MenuItem,
|
|
14
|
+
Select,
|
|
13
15
|
Stack,
|
|
14
16
|
ToggleButton,
|
|
15
17
|
ToggleButtonGroup,
|
|
@@ -17,7 +19,8 @@ import {
|
|
|
17
19
|
} from "@mui/material";
|
|
18
20
|
import { styled } from "@mui/system";
|
|
19
21
|
import { useSetState } from "ahooks";
|
|
20
|
-
import { useEffect, useState } from "react";
|
|
22
|
+
import { useEffect, useMemo, useState } from "react";
|
|
23
|
+
import { usePaymentContext } from "../contexts/payment.js";
|
|
21
24
|
import { formatError, formatPriceAmount, formatRecurring } from "../libs/util.js";
|
|
22
25
|
import Amount from "../payment/amount.js";
|
|
23
26
|
const groupItemsByRecurring = (items) => {
|
|
@@ -40,8 +43,31 @@ PricingTable.defaultProps = {
|
|
|
40
43
|
};
|
|
41
44
|
export default function PricingTable({ table, alignItems, interval, mode, onSelect }) {
|
|
42
45
|
const { t, locale } = useLocaleContext();
|
|
46
|
+
const {
|
|
47
|
+
settings: { paymentMethods = [] }
|
|
48
|
+
} = usePaymentContext();
|
|
49
|
+
const [currency, setCurrency] = useState(table.currency);
|
|
43
50
|
const { recurring, grouped } = groupItemsByRecurring(table.items);
|
|
44
51
|
const [state, setState] = useSetState({ interval });
|
|
52
|
+
const currencyMap = useMemo(() => {
|
|
53
|
+
const { payment_currencies: paymentCurrencies = [] } = paymentMethods[0];
|
|
54
|
+
return paymentCurrencies.reduce((acc, x) => {
|
|
55
|
+
acc[x.id] = x;
|
|
56
|
+
return acc;
|
|
57
|
+
}, {});
|
|
58
|
+
}, [paymentMethods]);
|
|
59
|
+
const currencyList = useMemo(() => {
|
|
60
|
+
const visited = {};
|
|
61
|
+
if (!state.interval) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
grouped[state.interval].forEach((x) => {
|
|
65
|
+
x.price.currency_options.forEach((c) => {
|
|
66
|
+
visited[c?.currency_id] = true;
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
return Object.keys(visited).map((x) => currencyMap[x]);
|
|
70
|
+
}, [currencyMap, grouped, state.interval]);
|
|
45
71
|
useEffect(() => {
|
|
46
72
|
if (table) {
|
|
47
73
|
if (!state.interval || !grouped[state.interval]) {
|
|
@@ -53,10 +79,21 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
53
79
|
}
|
|
54
80
|
}, [table]);
|
|
55
81
|
const Root = styled(Box)`
|
|
82
|
+
.btn-row {
|
|
83
|
+
display: flex;
|
|
84
|
+
flex-wrap: wrap;
|
|
85
|
+
justify-content: space-between;
|
|
86
|
+
align-items: center;
|
|
87
|
+
width: 100%;
|
|
88
|
+
gap: 20px;
|
|
89
|
+
}
|
|
56
90
|
@media (max-width: ${({ theme }) => theme.breakpoints.values.sm}px) {
|
|
57
91
|
.price-table-item {
|
|
58
92
|
width: 90% !important;
|
|
59
93
|
}
|
|
94
|
+
.btn-row {
|
|
95
|
+
padding: 0 20px;
|
|
96
|
+
}
|
|
60
97
|
}
|
|
61
98
|
@media (min-width: ${({ theme }) => theme.breakpoints.values.md}px) {
|
|
62
99
|
.price-table-wrap:has(> div:nth-child(1)) {
|
|
@@ -82,47 +119,59 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
82
119
|
}
|
|
83
120
|
},
|
|
84
121
|
children: [
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
onChange: (_, value) => {
|
|
99
|
-
if (value !== null) {
|
|
100
|
-
setState({ interval: value });
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
exclusive: true,
|
|
104
|
-
children: Object.keys(recurring).map((x) => /* @__PURE__ */ jsx(
|
|
105
|
-
ToggleButton,
|
|
106
|
-
{
|
|
107
|
-
size: "small",
|
|
108
|
-
value: x,
|
|
109
|
-
sx: {
|
|
110
|
-
textTransform: "capitalize",
|
|
111
|
-
padding: "5px 12px",
|
|
112
|
-
fontSize: "13px",
|
|
113
|
-
backgroundColor: x === state.interval ? "#fff !important" : "#f1f3f5 !important",
|
|
114
|
-
border: "0px",
|
|
115
|
-
"&.Mui-selected": {
|
|
116
|
-
borderRadius: "9999px !important",
|
|
117
|
-
border: "1px solid #e5e7eb"
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
children: formatRecurring(recurring[x], true, "", locale)
|
|
122
|
+
/* @__PURE__ */ jsxs("div", { className: "btn-row", children: [
|
|
123
|
+
Object.keys(recurring).length > 1 && /* @__PURE__ */ jsx(
|
|
124
|
+
ToggleButtonGroup,
|
|
125
|
+
{
|
|
126
|
+
size: "small",
|
|
127
|
+
value: state.interval,
|
|
128
|
+
sx: {
|
|
129
|
+
padding: "4px",
|
|
130
|
+
borderRadius: "36px",
|
|
131
|
+
height: "40px",
|
|
132
|
+
boxSizing: "border-box",
|
|
133
|
+
backgroundColor: "#f1f3f5",
|
|
134
|
+
border: 0
|
|
121
135
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
onChange: (_, value) => {
|
|
137
|
+
if (value !== null) {
|
|
138
|
+
setState({ interval: value });
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
exclusive: true,
|
|
142
|
+
children: Object.keys(recurring).map((x) => /* @__PURE__ */ jsx(
|
|
143
|
+
ToggleButton,
|
|
144
|
+
{
|
|
145
|
+
size: "small",
|
|
146
|
+
value: x,
|
|
147
|
+
sx: {
|
|
148
|
+
textTransform: "capitalize",
|
|
149
|
+
padding: "5px 12px",
|
|
150
|
+
fontSize: "13px",
|
|
151
|
+
backgroundColor: x === state.interval ? "#fff !important" : "#f1f3f5 !important",
|
|
152
|
+
border: "0px",
|
|
153
|
+
"&.Mui-selected": {
|
|
154
|
+
borderRadius: "9999px !important",
|
|
155
|
+
border: "1px solid #e5e7eb"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
children: formatRecurring(recurring[x], true, "", locale)
|
|
159
|
+
},
|
|
160
|
+
x
|
|
161
|
+
))
|
|
162
|
+
}
|
|
163
|
+
),
|
|
164
|
+
currencyList.length > 1 && /* @__PURE__ */ jsx(
|
|
165
|
+
Select,
|
|
166
|
+
{
|
|
167
|
+
value: currency.id,
|
|
168
|
+
onChange: (e) => setCurrency(currencyList.find((v) => v.id === e.target.value)),
|
|
169
|
+
size: "small",
|
|
170
|
+
sx: { m: 1, width: 120 },
|
|
171
|
+
children: currencyList.map((x) => /* @__PURE__ */ jsx(MenuItem, { value: x.id, children: /* @__PURE__ */ jsx(Typography, { color: x.id === currency.id ? "text.primary" : "text.secondary", children: x.symbol }) }, x.id))
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
] }),
|
|
126
175
|
/* @__PURE__ */ jsx(
|
|
127
176
|
Stack,
|
|
128
177
|
{
|
|
@@ -204,7 +253,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
204
253
|
/* @__PURE__ */ jsx(
|
|
205
254
|
Amount,
|
|
206
255
|
{
|
|
207
|
-
amount: formatPriceAmount(x.price,
|
|
256
|
+
amount: formatPriceAmount(x.price, currency, x.product.unit_label),
|
|
208
257
|
sx: { my: 0, marginTop: "0px !important", fontSize: "48px", fontWeight: "bold" }
|
|
209
258
|
}
|
|
210
259
|
),
|
|
@@ -265,7 +314,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
265
314
|
)
|
|
266
315
|
] }, f.name))
|
|
267
316
|
] }) }),
|
|
268
|
-
/* @__PURE__ */ jsx(Subscribe, { x, action, onSelect })
|
|
317
|
+
/* @__PURE__ */ jsx(Subscribe, { x, action, onSelect, currencyId: currency.id })
|
|
269
318
|
]
|
|
270
319
|
},
|
|
271
320
|
x?.price_id
|
|
@@ -278,12 +327,12 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
278
327
|
}
|
|
279
328
|
) });
|
|
280
329
|
}
|
|
281
|
-
function Subscribe({ x, action, onSelect }) {
|
|
330
|
+
function Subscribe({ x, action, onSelect, currencyId }) {
|
|
282
331
|
const [state, setState] = useState({ loading: "", loaded: false });
|
|
283
332
|
const handleSelect = async (priceId) => {
|
|
284
333
|
try {
|
|
285
334
|
setState({ loading: priceId, loaded: true });
|
|
286
|
-
await onSelect(priceId);
|
|
335
|
+
await onSelect(priceId, currencyId);
|
|
287
336
|
} catch (err) {
|
|
288
337
|
console.error(err);
|
|
289
338
|
Toast.error(formatError(err));
|
package/es/libs/util.d.ts
CHANGED
package/es/libs/util.js
CHANGED
|
@@ -205,11 +205,9 @@ export function formatLineItemPricing(item, currency, trial, locale = "en") {
|
|
|
205
205
|
if (price.recurring?.usage_type === "metered" || +item.quantity === 1) {
|
|
206
206
|
quantity = "";
|
|
207
207
|
}
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
)} ${currency.symbol}`;
|
|
212
|
-
const unit = `${fromUnitToToken(getPriceUintAmountByCurrency(price, currency))} ${currency.symbol}`;
|
|
208
|
+
const unitValue = new BN(getPriceUintAmountByCurrency(price, currency));
|
|
209
|
+
const total = `${fromUnitToToken(unitValue.mul(new BN(item.quantity)), currency.decimal)} ${currency.symbol}`;
|
|
210
|
+
const unit = `${fromUnitToToken(unitValue, currency.decimal)} ${currency.symbol}`;
|
|
213
211
|
const appendUnit = (v, alt) => {
|
|
214
212
|
if (price.product.unit_label) {
|
|
215
213
|
return `${v}/${price.product.unit_label}`;
|
|
@@ -609,3 +607,11 @@ export const getTxLink = (method, details) => {
|
|
|
609
607
|
}
|
|
610
608
|
return { text: "N/A", link: "", gas: "" };
|
|
611
609
|
};
|
|
610
|
+
export function getQueryParams(url) {
|
|
611
|
+
const queryParams = {};
|
|
612
|
+
const urlObj = new URL(url);
|
|
613
|
+
urlObj.searchParams.forEach((value, key) => {
|
|
614
|
+
queryParams[key] = value;
|
|
615
|
+
});
|
|
616
|
+
return queryParams;
|
|
617
|
+
}
|
package/es/payment/form/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import FormInput from "../../components/input.js";
|
|
|
18
18
|
import { usePaymentContext } from "../../contexts/payment.js";
|
|
19
19
|
import { useSubscription } from "../../hooks/subscription.js";
|
|
20
20
|
import api from "../../libs/api.js";
|
|
21
|
-
import { flattenPaymentMethods, formatError, getPrefix, getStatementDescriptor } from "../../libs/util.js";
|
|
21
|
+
import { flattenPaymentMethods, formatError, getPrefix, getQueryParams, getStatementDescriptor } from "../../libs/util.js";
|
|
22
22
|
import UserButtons from "./addon.js";
|
|
23
23
|
import AddressForm from "./address.js";
|
|
24
24
|
import CurrencySelector from "./currency.js";
|
|
@@ -72,7 +72,11 @@ export default function PaymentForm({
|
|
|
72
72
|
stripePaying: false
|
|
73
73
|
});
|
|
74
74
|
const currencies = flattenPaymentMethods(paymentMethods);
|
|
75
|
-
const [paymentCurrencyIndex, setPaymentCurrencyIndex] = useState(
|
|
75
|
+
const [paymentCurrencyIndex, setPaymentCurrencyIndex] = useState(() => {
|
|
76
|
+
const query = getQueryParams(window.location.href);
|
|
77
|
+
const index = currencies.findIndex((x) => x.id === query.currencyId);
|
|
78
|
+
return index >= 0 ? index : 0;
|
|
79
|
+
});
|
|
76
80
|
const onCheckoutComplete = useMemoizedFn(async ({ response }) => {
|
|
77
81
|
if (response.id === checkoutSession.id && state.paid === false) {
|
|
78
82
|
await handleConnected();
|
package/lib/checkout/table.js
CHANGED
|
@@ -60,10 +60,14 @@ function CheckoutTable({
|
|
|
60
60
|
children: t("payment.checkout.noPricing")
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
const handleSelect = priceId => {
|
|
63
|
+
const handleSelect = (priceId, currencyId) => {
|
|
64
64
|
_api.default.post(`/api/pricing-tables/${data.id}/checkout/${priceId}?${(0, _util.mergeExtraParams)(extraParams)}`).then(res => {
|
|
65
65
|
if (mode === "standalone") {
|
|
66
|
-
|
|
66
|
+
let {
|
|
67
|
+
url
|
|
68
|
+
} = res.data;
|
|
69
|
+
url = url.indexOf("?") > -1 ? `${url}¤cyId=${currencyId}` : `${url}?currencyId=${currencyId}`;
|
|
70
|
+
window.location.replace(url);
|
|
67
71
|
} else {
|
|
68
72
|
window.location.hash = res.data.id;
|
|
69
73
|
setSessionId(res.data.id);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { TPricingTableExpanded } from '@blocklet/payment-types';
|
|
3
3
|
type Props = {
|
|
4
4
|
table: TPricingTableExpanded;
|
|
5
|
-
onSelect: (priceId: string) => void;
|
|
5
|
+
onSelect: (priceId: string, currencyId: string) => void;
|
|
6
6
|
alignItems?: 'center' | 'left';
|
|
7
7
|
mode?: 'checkout' | 'select';
|
|
8
8
|
interval?: string;
|
|
@@ -13,6 +13,7 @@ var _material = require("@mui/material");
|
|
|
13
13
|
var _system = require("@mui/system");
|
|
14
14
|
var _ahooks = require("ahooks");
|
|
15
15
|
var _react = require("react");
|
|
16
|
+
var _payment = require("../contexts/payment");
|
|
16
17
|
var _util = require("../libs/util");
|
|
17
18
|
var _amount = _interopRequireDefault(require("../payment/amount"));
|
|
18
19
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -48,6 +49,12 @@ function PricingTable({
|
|
|
48
49
|
t,
|
|
49
50
|
locale
|
|
50
51
|
} = (0, _context.useLocaleContext)();
|
|
52
|
+
const {
|
|
53
|
+
settings: {
|
|
54
|
+
paymentMethods = []
|
|
55
|
+
}
|
|
56
|
+
} = (0, _payment.usePaymentContext)();
|
|
57
|
+
const [currency, setCurrency] = (0, _react.useState)(table.currency);
|
|
51
58
|
const {
|
|
52
59
|
recurring,
|
|
53
60
|
grouped
|
|
@@ -55,6 +62,27 @@ function PricingTable({
|
|
|
55
62
|
const [state, setState] = (0, _ahooks.useSetState)({
|
|
56
63
|
interval
|
|
57
64
|
});
|
|
65
|
+
const currencyMap = (0, _react.useMemo)(() => {
|
|
66
|
+
const {
|
|
67
|
+
payment_currencies: paymentCurrencies = []
|
|
68
|
+
} = paymentMethods[0];
|
|
69
|
+
return paymentCurrencies.reduce((acc, x) => {
|
|
70
|
+
acc[x.id] = x;
|
|
71
|
+
return acc;
|
|
72
|
+
}, {});
|
|
73
|
+
}, [paymentMethods]);
|
|
74
|
+
const currencyList = (0, _react.useMemo)(() => {
|
|
75
|
+
const visited = {};
|
|
76
|
+
if (!state.interval) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
grouped[state.interval].forEach(x => {
|
|
80
|
+
x.price.currency_options.forEach(c => {
|
|
81
|
+
visited[c?.currency_id] = true;
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
return Object.keys(visited).map(x => currencyMap[x]);
|
|
85
|
+
}, [currencyMap, grouped, state.interval]);
|
|
58
86
|
(0, _react.useEffect)(() => {
|
|
59
87
|
if (table) {
|
|
60
88
|
if (!state.interval || !grouped[state.interval]) {
|
|
@@ -68,12 +96,23 @@ function PricingTable({
|
|
|
68
96
|
}
|
|
69
97
|
}, [table]);
|
|
70
98
|
const Root = (0, _system.styled)(_material.Box)`
|
|
99
|
+
.btn-row {
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-wrap: wrap;
|
|
102
|
+
justify-content: space-between;
|
|
103
|
+
align-items: center;
|
|
104
|
+
width: 100%;
|
|
105
|
+
gap: 20px;
|
|
106
|
+
}
|
|
71
107
|
@media (max-width: ${({
|
|
72
108
|
theme
|
|
73
109
|
}) => theme.breakpoints.values.sm}px) {
|
|
74
110
|
.price-table-item {
|
|
75
111
|
width: 90% !important;
|
|
76
112
|
}
|
|
113
|
+
.btn-row {
|
|
114
|
+
padding: 0 20px;
|
|
115
|
+
}
|
|
77
116
|
}
|
|
78
117
|
@media (min-width: ${({
|
|
79
118
|
theme
|
|
@@ -99,41 +138,59 @@ function PricingTable({
|
|
|
99
138
|
sm: mode === "select" ? 3 : 5
|
|
100
139
|
}
|
|
101
140
|
},
|
|
102
|
-
children: [
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
sx: {
|
|
106
|
-
padding: "4px",
|
|
107
|
-
borderRadius: "36px",
|
|
108
|
-
height: "40px",
|
|
109
|
-
boxSizing: "border-box",
|
|
110
|
-
backgroundColor: "#f1f3f5",
|
|
111
|
-
border: 0
|
|
112
|
-
},
|
|
113
|
-
onChange: (_, value) => {
|
|
114
|
-
if (value !== null) {
|
|
115
|
-
setState({
|
|
116
|
-
interval: value
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
exclusive: true,
|
|
121
|
-
children: Object.keys(recurring).map(x => /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ToggleButton, {
|
|
141
|
+
children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)("div", {
|
|
142
|
+
className: "btn-row",
|
|
143
|
+
children: [Object.keys(recurring).length > 1 && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ToggleButtonGroup, {
|
|
122
144
|
size: "small",
|
|
123
|
-
value:
|
|
145
|
+
value: state.interval,
|
|
124
146
|
sx: {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
147
|
+
padding: "4px",
|
|
148
|
+
borderRadius: "36px",
|
|
149
|
+
height: "40px",
|
|
150
|
+
boxSizing: "border-box",
|
|
151
|
+
backgroundColor: "#f1f3f5",
|
|
152
|
+
border: 0
|
|
153
|
+
},
|
|
154
|
+
onChange: (_, value) => {
|
|
155
|
+
if (value !== null) {
|
|
156
|
+
setState({
|
|
157
|
+
interval: value
|
|
158
|
+
});
|
|
133
159
|
}
|
|
134
160
|
},
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
exclusive: true,
|
|
162
|
+
children: Object.keys(recurring).map(x => /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.ToggleButton, {
|
|
163
|
+
size: "small",
|
|
164
|
+
value: x,
|
|
165
|
+
sx: {
|
|
166
|
+
textTransform: "capitalize",
|
|
167
|
+
padding: "5px 12px",
|
|
168
|
+
fontSize: "13px",
|
|
169
|
+
backgroundColor: x === state.interval ? "#fff !important" : "#f1f3f5 !important",
|
|
170
|
+
border: "0px",
|
|
171
|
+
"&.Mui-selected": {
|
|
172
|
+
borderRadius: "9999px !important",
|
|
173
|
+
border: "1px solid #e5e7eb"
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
children: (0, _util.formatRecurring)(recurring[x], true, "", locale)
|
|
177
|
+
}, x))
|
|
178
|
+
}), currencyList.length > 1 && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Select, {
|
|
179
|
+
value: currency.id,
|
|
180
|
+
onChange: e => setCurrency(currencyList.find(v => v.id === e.target.value)),
|
|
181
|
+
size: "small",
|
|
182
|
+
sx: {
|
|
183
|
+
m: 1,
|
|
184
|
+
width: 120
|
|
185
|
+
},
|
|
186
|
+
children: currencyList.map(x => /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.MenuItem, {
|
|
187
|
+
value: x.id,
|
|
188
|
+
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
189
|
+
color: x.id === currency.id ? "text.primary" : "text.secondary",
|
|
190
|
+
children: x.symbol
|
|
191
|
+
})
|
|
192
|
+
}, x.id))
|
|
193
|
+
})]
|
|
137
194
|
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Stack, {
|
|
138
195
|
flexWrap: "wrap",
|
|
139
196
|
direction: "row",
|
|
@@ -205,7 +262,7 @@ function PricingTable({
|
|
|
205
262
|
}
|
|
206
263
|
})]
|
|
207
264
|
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_amount.default, {
|
|
208
|
-
amount: (0, _util.formatPriceAmount)(x.price,
|
|
265
|
+
amount: (0, _util.formatPriceAmount)(x.price, currency, x.product.unit_label),
|
|
209
266
|
sx: {
|
|
210
267
|
my: 0,
|
|
211
268
|
marginTop: "0px !important",
|
|
@@ -279,7 +336,8 @@ function PricingTable({
|
|
|
279
336
|
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(Subscribe, {
|
|
280
337
|
x,
|
|
281
338
|
action,
|
|
282
|
-
onSelect
|
|
339
|
+
onSelect,
|
|
340
|
+
currencyId: currency.id
|
|
283
341
|
})]
|
|
284
342
|
}, x?.price_id);
|
|
285
343
|
})
|
|
@@ -290,7 +348,8 @@ function PricingTable({
|
|
|
290
348
|
function Subscribe({
|
|
291
349
|
x,
|
|
292
350
|
action,
|
|
293
|
-
onSelect
|
|
351
|
+
onSelect,
|
|
352
|
+
currencyId
|
|
294
353
|
}) {
|
|
295
354
|
const [state, setState] = (0, _react.useState)({
|
|
296
355
|
loading: "",
|
|
@@ -302,7 +361,7 @@ function Subscribe({
|
|
|
302
361
|
loading: priceId,
|
|
303
362
|
loaded: true
|
|
304
363
|
});
|
|
305
|
-
await onSelect(priceId);
|
|
364
|
+
await onSelect(priceId, currencyId);
|
|
306
365
|
} catch (err) {
|
|
307
366
|
console.error(err);
|
|
308
367
|
_Toast.default.error((0, _util.formatError)(err));
|
package/lib/libs/util.d.ts
CHANGED
package/lib/libs/util.js
CHANGED
|
@@ -28,6 +28,7 @@ exports.getPayoutStatusColor = getPayoutStatusColor;
|
|
|
28
28
|
exports.getPrefix = void 0;
|
|
29
29
|
exports.getPriceCurrencyOptions = getPriceCurrencyOptions;
|
|
30
30
|
exports.getPriceUintAmountByCurrency = getPriceUintAmountByCurrency;
|
|
31
|
+
exports.getQueryParams = getQueryParams;
|
|
31
32
|
exports.getRecurringPeriod = getRecurringPeriod;
|
|
32
33
|
exports.getRefundStatusColor = getRefundStatusColor;
|
|
33
34
|
exports.getStatementDescriptor = getStatementDescriptor;
|
|
@@ -263,8 +264,9 @@ function formatLineItemPricing(item, currency, trial, locale = "en") {
|
|
|
263
264
|
if (price.recurring?.usage_type === "metered" || +item.quantity === 1) {
|
|
264
265
|
quantity = "";
|
|
265
266
|
}
|
|
266
|
-
const
|
|
267
|
-
const
|
|
267
|
+
const unitValue = new _util.BN(getPriceUintAmountByCurrency(price, currency));
|
|
268
|
+
const total = `${(0, _util.fromUnitToToken)(unitValue.mul(new _util.BN(item.quantity)), currency.decimal)} ${currency.symbol}`;
|
|
269
|
+
const unit = `${(0, _util.fromUnitToToken)(unitValue, currency.decimal)} ${currency.symbol}`;
|
|
268
270
|
const appendUnit = (v, alt) => {
|
|
269
271
|
if (price.product.unit_label) {
|
|
270
272
|
return `${v}/${price.product.unit_label}`;
|
|
@@ -724,4 +726,12 @@ const getTxLink = (method, details) => {
|
|
|
724
726
|
gas: ""
|
|
725
727
|
};
|
|
726
728
|
};
|
|
727
|
-
exports.getTxLink = getTxLink;
|
|
729
|
+
exports.getTxLink = getTxLink;
|
|
730
|
+
function getQueryParams(url) {
|
|
731
|
+
const queryParams = {};
|
|
732
|
+
const urlObj = new URL(url);
|
|
733
|
+
urlObj.searchParams.forEach((value, key) => {
|
|
734
|
+
queryParams[key] = value;
|
|
735
|
+
});
|
|
736
|
+
return queryParams;
|
|
737
|
+
}
|
|
@@ -91,7 +91,11 @@ function PaymentForm({
|
|
|
91
91
|
stripePaying: false
|
|
92
92
|
});
|
|
93
93
|
const currencies = (0, _util.flattenPaymentMethods)(paymentMethods);
|
|
94
|
-
const [paymentCurrencyIndex, setPaymentCurrencyIndex] = (0, _react.useState)(
|
|
94
|
+
const [paymentCurrencyIndex, setPaymentCurrencyIndex] = (0, _react.useState)(() => {
|
|
95
|
+
const query = (0, _util.getQueryParams)(window.location.href);
|
|
96
|
+
const index = currencies.findIndex(x => x.id === query.currencyId);
|
|
97
|
+
return index >= 0 ? index : 0;
|
|
98
|
+
});
|
|
95
99
|
const onCheckoutComplete = (0, _ahooks.useMemoizedFn)(async ({
|
|
96
100
|
response
|
|
97
101
|
}) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/payment-react",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.284",
|
|
4
4
|
"description": "Reusable react components for payment kit v2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"@babel/core": "^7.24.7",
|
|
92
92
|
"@babel/preset-env": "^7.24.7",
|
|
93
93
|
"@babel/preset-react": "^7.24.7",
|
|
94
|
-
"@blocklet/payment-types": "1.13.
|
|
94
|
+
"@blocklet/payment-types": "1.13.284",
|
|
95
95
|
"@storybook/addon-essentials": "^7.6.19",
|
|
96
96
|
"@storybook/addon-interactions": "^7.6.19",
|
|
97
97
|
"@storybook/addon-links": "^7.6.19",
|
|
@@ -120,5 +120,5 @@
|
|
|
120
120
|
"vite-plugin-babel": "^1.2.0",
|
|
121
121
|
"vite-plugin-node-polyfills": "^0.21.0"
|
|
122
122
|
},
|
|
123
|
-
"gitHead": "
|
|
123
|
+
"gitHead": "c844c5590104d35d4bffedddec6f909684bacc4b"
|
|
124
124
|
}
|
package/src/checkout/table.tsx
CHANGED
|
@@ -41,12 +41,14 @@ export default function CheckoutTable({ id, mode, onPaid, onError, onChange, ext
|
|
|
41
41
|
return <Alert severity="warning">{t('payment.checkout.noPricing')}</Alert>;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const handleSelect = (priceId: string) => {
|
|
44
|
+
const handleSelect = (priceId: string, currencyId: string) => {
|
|
45
45
|
api
|
|
46
46
|
.post(`/api/pricing-tables/${data.id}/checkout/${priceId}?${mergeExtraParams(extraParams)}`)
|
|
47
47
|
.then((res) => {
|
|
48
48
|
if (mode === 'standalone') {
|
|
49
|
-
|
|
49
|
+
let { url } = res.data;
|
|
50
|
+
url = url.indexOf('?') > -1 ? `${url}¤cyId=${currencyId}` : `${url}?currencyId=${currencyId}`;
|
|
51
|
+
window.location.replace(url);
|
|
50
52
|
} else {
|
|
51
53
|
window.location.hash = res.data.id;
|
|
52
54
|
setSessionId(res.data.id);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-nested-ternary */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
3
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
4
|
-
import type { PriceRecurring, TPricingTableExpanded, TPricingTableItem } from '@blocklet/payment-types';
|
|
4
|
+
import type { PriceCurrency, PriceRecurring, TPricingTableExpanded, TPricingTableItem } from '@blocklet/payment-types';
|
|
5
5
|
import { CheckOutlined } from '@mui/icons-material';
|
|
6
6
|
import { LoadingButton } from '@mui/lab';
|
|
7
7
|
import {
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
ListItem,
|
|
12
12
|
ListItemIcon,
|
|
13
13
|
ListItemText,
|
|
14
|
+
MenuItem,
|
|
15
|
+
Select,
|
|
14
16
|
Stack,
|
|
15
17
|
ToggleButton,
|
|
16
18
|
ToggleButtonGroup,
|
|
@@ -18,8 +20,9 @@ import {
|
|
|
18
20
|
} from '@mui/material';
|
|
19
21
|
import { styled } from '@mui/system';
|
|
20
22
|
import { useSetState } from 'ahooks';
|
|
21
|
-
import { useEffect, useState } from 'react';
|
|
23
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
22
24
|
|
|
25
|
+
import { usePaymentContext } from '../contexts/payment';
|
|
23
26
|
import { formatError, formatPriceAmount, formatRecurring } from '../libs/util';
|
|
24
27
|
import Amount from '../payment/amount';
|
|
25
28
|
|
|
@@ -44,7 +47,7 @@ const groupItemsByRecurring = (items: TPricingTableItem[]) => {
|
|
|
44
47
|
|
|
45
48
|
type Props = {
|
|
46
49
|
table: TPricingTableExpanded;
|
|
47
|
-
onSelect: (priceId: string) => void;
|
|
50
|
+
onSelect: (priceId: string, currencyId: string) => void;
|
|
48
51
|
alignItems?: 'center' | 'left';
|
|
49
52
|
mode?: 'checkout' | 'select';
|
|
50
53
|
interval?: string;
|
|
@@ -58,8 +61,32 @@ PricingTable.defaultProps = {
|
|
|
58
61
|
|
|
59
62
|
export default function PricingTable({ table, alignItems, interval, mode, onSelect }: Props) {
|
|
60
63
|
const { t, locale } = useLocaleContext();
|
|
64
|
+
const {
|
|
65
|
+
settings: { paymentMethods = [] },
|
|
66
|
+
} = usePaymentContext();
|
|
67
|
+
const [currency, setCurrency] = useState(table.currency);
|
|
61
68
|
const { recurring, grouped } = groupItemsByRecurring(table.items);
|
|
62
69
|
const [state, setState] = useSetState({ interval });
|
|
70
|
+
const currencyMap = useMemo(() => {
|
|
71
|
+
const { payment_currencies: paymentCurrencies = [] } = paymentMethods[0];
|
|
72
|
+
return paymentCurrencies.reduce((acc: any, x: { id: string; symbol: string }) => {
|
|
73
|
+
acc[x.id] = x;
|
|
74
|
+
return acc;
|
|
75
|
+
}, {});
|
|
76
|
+
}, [paymentMethods]);
|
|
77
|
+
|
|
78
|
+
const currencyList = useMemo(() => {
|
|
79
|
+
const visited: { [key: string]: boolean } = {};
|
|
80
|
+
if (!state.interval) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
grouped[state.interval].forEach((x: TPricingTableItem) => {
|
|
84
|
+
x.price.currency_options.forEach((c: PriceCurrency) => {
|
|
85
|
+
visited[c?.currency_id] = true;
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
return Object.keys(visited).map((x) => currencyMap[x]);
|
|
89
|
+
}, [currencyMap, grouped, state.interval]);
|
|
63
90
|
|
|
64
91
|
useEffect(() => {
|
|
65
92
|
if (table) {
|
|
@@ -74,10 +101,21 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
74
101
|
}, [table]);
|
|
75
102
|
|
|
76
103
|
const Root = styled(Box)`
|
|
104
|
+
.btn-row {
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-wrap: wrap;
|
|
107
|
+
justify-content: space-between;
|
|
108
|
+
align-items: center;
|
|
109
|
+
width: 100%;
|
|
110
|
+
gap: 20px;
|
|
111
|
+
}
|
|
77
112
|
@media (max-width: ${({ theme }) => theme.breakpoints.values.sm}px) {
|
|
78
113
|
.price-table-item {
|
|
79
114
|
width: 90% !important;
|
|
80
115
|
}
|
|
116
|
+
.btn-row {
|
|
117
|
+
padding: 0 20px;
|
|
118
|
+
}
|
|
81
119
|
}
|
|
82
120
|
@media (min-width: ${({ theme }) => theme.breakpoints.values.md}px) {
|
|
83
121
|
.price-table-wrap:has(> div:nth-child(1)) {
|
|
@@ -102,45 +140,60 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
102
140
|
sm: mode === 'select' ? 3 : 5,
|
|
103
141
|
},
|
|
104
142
|
}}>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
<div className="btn-row">
|
|
144
|
+
{Object.keys(recurring).length > 1 && (
|
|
145
|
+
<ToggleButtonGroup
|
|
146
|
+
size="small"
|
|
147
|
+
value={state.interval}
|
|
148
|
+
sx={{
|
|
149
|
+
padding: '4px',
|
|
150
|
+
borderRadius: '36px',
|
|
151
|
+
height: '40px',
|
|
152
|
+
boxSizing: 'border-box',
|
|
153
|
+
backgroundColor: '#f1f3f5',
|
|
154
|
+
border: 0,
|
|
155
|
+
}}
|
|
156
|
+
onChange={(_, value) => {
|
|
157
|
+
if (value !== null) {
|
|
158
|
+
setState({ interval: value });
|
|
159
|
+
}
|
|
160
|
+
}}
|
|
161
|
+
exclusive>
|
|
162
|
+
{Object.keys(recurring).map((x) => (
|
|
163
|
+
<ToggleButton
|
|
164
|
+
size="small"
|
|
165
|
+
key={x}
|
|
166
|
+
value={x}
|
|
167
|
+
sx={{
|
|
168
|
+
textTransform: 'capitalize',
|
|
169
|
+
padding: '5px 12px',
|
|
170
|
+
fontSize: '13px',
|
|
171
|
+
backgroundColor: x === state.interval ? '#fff !important' : '#f1f3f5 !important',
|
|
172
|
+
border: '0px',
|
|
173
|
+
'&.Mui-selected': {
|
|
174
|
+
borderRadius: '9999px !important',
|
|
175
|
+
border: '1px solid #e5e7eb',
|
|
176
|
+
},
|
|
177
|
+
}}>
|
|
178
|
+
{formatRecurring(recurring[x] as PriceRecurring, true, '', locale)}
|
|
179
|
+
</ToggleButton>
|
|
180
|
+
))}
|
|
181
|
+
</ToggleButtonGroup>
|
|
182
|
+
)}
|
|
183
|
+
{currencyList.length > 1 && (
|
|
184
|
+
<Select
|
|
185
|
+
value={currency.id}
|
|
186
|
+
onChange={(e) => setCurrency(currencyList.find((v) => v.id === e.target.value))}
|
|
187
|
+
size="small"
|
|
188
|
+
sx={{ m: 1, width: 120 }}>
|
|
189
|
+
{currencyList.map((x) => (
|
|
190
|
+
<MenuItem key={x.id} value={x.id}>
|
|
191
|
+
<Typography color={x.id === currency.id ? 'text.primary' : 'text.secondary'}>{x.symbol}</Typography>
|
|
192
|
+
</MenuItem>
|
|
193
|
+
))}
|
|
194
|
+
</Select>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
144
197
|
<Stack
|
|
145
198
|
flexWrap="wrap"
|
|
146
199
|
direction="row"
|
|
@@ -216,7 +269,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
216
269
|
)}
|
|
217
270
|
</Box>
|
|
218
271
|
<Amount
|
|
219
|
-
amount={formatPriceAmount(x.price,
|
|
272
|
+
amount={formatPriceAmount(x.price, currency, x.product.unit_label)}
|
|
220
273
|
sx={{ my: 0, marginTop: '0px !important', fontSize: '48px', fontWeight: 'bold' }}
|
|
221
274
|
/>
|
|
222
275
|
<Typography
|
|
@@ -271,7 +324,7 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
271
324
|
</List>
|
|
272
325
|
</Box>
|
|
273
326
|
)}
|
|
274
|
-
<Subscribe x={x} action={action} onSelect={onSelect} />
|
|
327
|
+
<Subscribe x={x} action={action} onSelect={onSelect} currencyId={currency.id} />
|
|
275
328
|
</Stack>
|
|
276
329
|
);
|
|
277
330
|
}
|
|
@@ -282,13 +335,13 @@ export default function PricingTable({ table, alignItems, interval, mode, onSele
|
|
|
282
335
|
);
|
|
283
336
|
}
|
|
284
337
|
|
|
285
|
-
function Subscribe({ x, action, onSelect }: any) {
|
|
338
|
+
function Subscribe({ x, action, onSelect, currencyId }: any) {
|
|
286
339
|
const [state, setState] = useState({ loading: '', loaded: false });
|
|
287
340
|
|
|
288
341
|
const handleSelect = async (priceId: string) => {
|
|
289
342
|
try {
|
|
290
343
|
setState({ loading: priceId, loaded: true });
|
|
291
|
-
await onSelect(priceId);
|
|
344
|
+
await onSelect(priceId, currencyId);
|
|
292
345
|
} catch (err) {
|
|
293
346
|
console.error(err);
|
|
294
347
|
Toast.error(formatError(err));
|
package/src/libs/util.ts
CHANGED
|
@@ -298,11 +298,11 @@ export function formatLineItemPricing(
|
|
|
298
298
|
quantity = '';
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const unit = `${fromUnitToToken(
|
|
301
|
+
const unitValue = new BN(getPriceUintAmountByCurrency(price, currency));
|
|
302
|
+
|
|
303
|
+
const total = `${fromUnitToToken(unitValue.mul(new BN(item.quantity)), currency.decimal)} ${currency.symbol}`;
|
|
304
|
+
|
|
305
|
+
const unit = `${fromUnitToToken(unitValue, currency.decimal)} ${currency.symbol}`;
|
|
306
306
|
|
|
307
307
|
const appendUnit = (v: string, alt: string) => {
|
|
308
308
|
if (price.product.unit_label) {
|
|
@@ -793,3 +793,12 @@ export const getTxLink = (method: TPaymentMethod, details: PaymentDetails) => {
|
|
|
793
793
|
|
|
794
794
|
return { text: 'N/A', link: '', gas: '' };
|
|
795
795
|
};
|
|
796
|
+
|
|
797
|
+
export function getQueryParams(url: string): Record<string, string> {
|
|
798
|
+
const queryParams: Record<string, string> = {};
|
|
799
|
+
const urlObj = new URL(url);
|
|
800
|
+
urlObj.searchParams.forEach((value, key) => {
|
|
801
|
+
queryParams[key] = value;
|
|
802
|
+
});
|
|
803
|
+
return queryParams;
|
|
804
|
+
}
|
|
@@ -26,7 +26,7 @@ import FormInput from '../../components/input';
|
|
|
26
26
|
import { usePaymentContext } from '../../contexts/payment';
|
|
27
27
|
import { useSubscription } from '../../hooks/subscription';
|
|
28
28
|
import api from '../../libs/api';
|
|
29
|
-
import { flattenPaymentMethods, formatError, getPrefix, getStatementDescriptor } from '../../libs/util';
|
|
29
|
+
import { flattenPaymentMethods, formatError, getPrefix, getQueryParams, getStatementDescriptor } from '../../libs/util';
|
|
30
30
|
import { CheckoutCallbacks, CheckoutContext } from '../../types';
|
|
31
31
|
import UserButtons from './addon';
|
|
32
32
|
import AddressForm from './address';
|
|
@@ -119,7 +119,12 @@ export default function PaymentForm({
|
|
|
119
119
|
});
|
|
120
120
|
|
|
121
121
|
const currencies = flattenPaymentMethods(paymentMethods);
|
|
122
|
-
|
|
122
|
+
|
|
123
|
+
const [paymentCurrencyIndex, setPaymentCurrencyIndex] = useState(() => {
|
|
124
|
+
const query = getQueryParams(window.location.href);
|
|
125
|
+
const index = currencies.findIndex((x) => x.id === query.currencyId);
|
|
126
|
+
return index >= 0 ? index : 0;
|
|
127
|
+
});
|
|
123
128
|
|
|
124
129
|
const onCheckoutComplete = useMemoizedFn(async ({ response }: { response: TCheckoutSession }) => {
|
|
125
130
|
if (response.id === checkoutSession.id && state.paid === false) {
|