@blocklet/payment-react 1.18.24 → 1.18.26
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/donate.js +11 -1
- package/es/components/country-select.js +243 -21
- package/es/components/over-due-invoice-payment.d.ts +3 -1
- package/es/components/over-due-invoice-payment.js +6 -4
- package/es/contexts/payment.d.ts +2 -1
- package/es/contexts/payment.js +8 -1
- package/es/hooks/keyboard.js +3 -0
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/libs/api.js +4 -0
- package/es/libs/currency.d.ts +3 -0
- package/es/libs/currency.js +22 -0
- package/es/libs/phone-validator.js +2 -0
- package/es/libs/util.d.ts +2 -2
- package/es/libs/util.js +7 -4
- package/es/libs/validator.d.ts +1 -0
- package/es/libs/validator.js +70 -0
- package/es/payment/form/address.js +17 -3
- package/es/payment/form/index.js +10 -1
- package/es/payment/form/phone.js +12 -1
- package/es/payment/form/stripe/form.js +72 -15
- package/es/payment/index.js +33 -11
- package/es/payment/product-donation.js +110 -12
- package/es/types/shims.d.ts +2 -0
- package/lib/checkout/donate.js +11 -1
- package/lib/components/country-select.js +243 -39
- package/lib/components/over-due-invoice-payment.d.ts +3 -1
- package/lib/components/over-due-invoice-payment.js +7 -4
- package/lib/contexts/payment.d.ts +2 -1
- package/lib/contexts/payment.js +9 -1
- package/lib/hooks/keyboard.js +3 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +12 -0
- package/lib/libs/api.js +4 -0
- package/lib/libs/currency.d.ts +3 -0
- package/lib/libs/currency.js +31 -0
- package/lib/libs/phone-validator.js +1 -0
- package/lib/libs/util.d.ts +2 -2
- package/lib/libs/util.js +7 -4
- package/lib/libs/validator.d.ts +1 -0
- package/lib/libs/validator.js +20 -0
- package/lib/payment/form/address.js +15 -2
- package/lib/payment/form/index.js +12 -1
- package/lib/payment/form/phone.js +13 -1
- package/lib/payment/form/stripe/form.js +98 -29
- package/lib/payment/index.js +34 -10
- package/lib/payment/product-donation.js +106 -15
- package/lib/types/shims.d.ts +2 -0
- package/package.json +8 -8
- package/src/checkout/donate.tsx +11 -1
- package/src/components/country-select.tsx +265 -20
- package/src/components/over-due-invoice-payment.tsx +6 -2
- package/src/contexts/payment.tsx +11 -1
- package/src/hooks/keyboard.ts +5 -3
- package/src/index.ts +1 -0
- package/src/libs/api.ts +4 -1
- package/src/libs/currency.ts +25 -0
- package/src/libs/phone-validator.ts +1 -0
- package/src/libs/util.ts +18 -4
- package/src/libs/validator.ts +70 -0
- package/src/payment/form/address.tsx +17 -4
- package/src/payment/form/index.tsx +11 -1
- package/src/payment/form/phone.tsx +15 -1
- package/src/payment/form/stripe/form.tsx +104 -32
- package/src/payment/index.tsx +45 -14
- package/src/payment/product-donation.tsx +129 -10
- package/src/types/shims.d.ts +2 -0
package/es/checkout/donate.js
CHANGED
|
@@ -65,6 +65,7 @@ export function DonateDetails({ supporters = [], currency, method }) {
|
|
|
65
65
|
return /* @__PURE__ */ jsx(
|
|
66
66
|
Stack,
|
|
67
67
|
{
|
|
68
|
+
className: "cko-donate-details",
|
|
68
69
|
sx: {
|
|
69
70
|
width: "100%",
|
|
70
71
|
minWidth: "256px",
|
|
@@ -246,8 +247,17 @@ function SupporterAvatar({
|
|
|
246
247
|
onClose: () => setOpen(false),
|
|
247
248
|
sx: {
|
|
248
249
|
".MuiDialogContent-root": {
|
|
249
|
-
width:
|
|
250
|
+
width: {
|
|
251
|
+
xs: "100%",
|
|
252
|
+
md: "450px"
|
|
253
|
+
},
|
|
250
254
|
padding: "8px"
|
|
255
|
+
},
|
|
256
|
+
".cko-donate-details": {
|
|
257
|
+
maxHeight: {
|
|
258
|
+
xs: "100%",
|
|
259
|
+
md: "300px"
|
|
260
|
+
}
|
|
251
261
|
}
|
|
252
262
|
},
|
|
253
263
|
title: `${customersNum} supporter${customersNum > 1 ? "s" : ""}`,
|
|
@@ -1,10 +1,110 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, forwardRef } from "react";
|
|
3
|
-
import { Box, MenuItem, Select, Typography } from "@mui/material";
|
|
2
|
+
import { useMemo, forwardRef, useState, useEffect, useRef } from "react";
|
|
3
|
+
import { Box, MenuItem, Select, Typography, TextField } from "@mui/material";
|
|
4
4
|
import { useFormContext } from "react-hook-form";
|
|
5
5
|
import { FlagEmoji, defaultCountries, parseCountry } from "react-international-phone";
|
|
6
|
+
import { useMobile } from "../hooks/mobile.js";
|
|
6
7
|
const CountrySelect = forwardRef(({ value, onChange, name, sx }, ref) => {
|
|
7
8
|
const { setValue } = useFormContext();
|
|
9
|
+
const [open, setOpen] = useState(false);
|
|
10
|
+
const [searchText, setSearchText] = useState("");
|
|
11
|
+
const inputRef = useRef(null);
|
|
12
|
+
const menuRef = useRef(null);
|
|
13
|
+
const listRef = useRef(null);
|
|
14
|
+
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
15
|
+
const itemHeightRef = useRef(40);
|
|
16
|
+
const { isMobile } = useMobile();
|
|
17
|
+
const measuredRef = useRef(false);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!open)
|
|
20
|
+
return () => {
|
|
21
|
+
};
|
|
22
|
+
const handleResize = () => {
|
|
23
|
+
measuredRef.current = false;
|
|
24
|
+
};
|
|
25
|
+
window.addEventListener("resize", handleResize);
|
|
26
|
+
return () => {
|
|
27
|
+
window.removeEventListener("resize", handleResize);
|
|
28
|
+
};
|
|
29
|
+
}, [open]);
|
|
30
|
+
const scrollToTop = () => {
|
|
31
|
+
if (listRef.current) {
|
|
32
|
+
listRef.current.scrollTop = 0;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const measureItemHeight = () => {
|
|
36
|
+
if (!listRef.current || !open)
|
|
37
|
+
return;
|
|
38
|
+
const items = listRef.current.querySelectorAll(".MuiMenuItem-root");
|
|
39
|
+
if (items.length > 0) {
|
|
40
|
+
const firstItem = items[0];
|
|
41
|
+
if (firstItem.offsetHeight > 0) {
|
|
42
|
+
itemHeightRef.current = firstItem.offsetHeight;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const controlScrollPosition = (index) => {
|
|
47
|
+
if (!listRef.current)
|
|
48
|
+
return;
|
|
49
|
+
if (open && !measuredRef.current) {
|
|
50
|
+
measureItemHeight();
|
|
51
|
+
measuredRef.current = true;
|
|
52
|
+
}
|
|
53
|
+
const listHeight = listRef.current.clientHeight;
|
|
54
|
+
const targetPosition = index * itemHeightRef.current;
|
|
55
|
+
if (index < 2) {
|
|
56
|
+
listRef.current.scrollTop = 0;
|
|
57
|
+
} else if (index > filteredCountries.length - 3) {
|
|
58
|
+
listRef.current.scrollTop = listRef.current.scrollHeight - listHeight;
|
|
59
|
+
} else {
|
|
60
|
+
const scrollPosition = targetPosition - listHeight / 2 + itemHeightRef.current / 2;
|
|
61
|
+
listRef.current.scrollTop = Math.max(0, scrollPosition);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
let timeout = null;
|
|
66
|
+
if (open) {
|
|
67
|
+
timeout = setTimeout(() => {
|
|
68
|
+
scrollToTop();
|
|
69
|
+
if (!isMobile && inputRef.current) {
|
|
70
|
+
inputRef.current.focus();
|
|
71
|
+
}
|
|
72
|
+
}, 100);
|
|
73
|
+
} else {
|
|
74
|
+
setSearchText("");
|
|
75
|
+
setFocusedIndex(-1);
|
|
76
|
+
}
|
|
77
|
+
return () => {
|
|
78
|
+
if (timeout) {
|
|
79
|
+
clearTimeout(timeout);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}, [open, isMobile]);
|
|
83
|
+
const filteredCountries = useMemo(() => {
|
|
84
|
+
if (!searchText)
|
|
85
|
+
return defaultCountries;
|
|
86
|
+
return defaultCountries.filter((c) => {
|
|
87
|
+
const parsed = parseCountry(c);
|
|
88
|
+
return parsed.name.toLowerCase().includes(searchText.toLowerCase()) || parsed.iso2.toLowerCase().includes(searchText.toLowerCase()) || `+${parsed.dialCode}`.includes(searchText);
|
|
89
|
+
});
|
|
90
|
+
}, [searchText]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
scrollToTop();
|
|
93
|
+
setFocusedIndex(-1);
|
|
94
|
+
}, [searchText]);
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
let timeout = null;
|
|
97
|
+
if (focusedIndex >= 0) {
|
|
98
|
+
timeout = setTimeout(() => {
|
|
99
|
+
controlScrollPosition(focusedIndex);
|
|
100
|
+
}, 10);
|
|
101
|
+
}
|
|
102
|
+
return () => {
|
|
103
|
+
if (timeout) {
|
|
104
|
+
clearTimeout(timeout);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}, [focusedIndex, filteredCountries.length]);
|
|
8
108
|
const countryDetail = useMemo(() => {
|
|
9
109
|
const item = defaultCountries.find((v) => v[1] === value);
|
|
10
110
|
return value && item ? parseCountry(item) : { name: "" };
|
|
@@ -13,13 +113,62 @@ const CountrySelect = forwardRef(({ value, onChange, name, sx }, ref) => {
|
|
|
13
113
|
onChange(e.target.value);
|
|
14
114
|
setValue(name, e.target.value);
|
|
15
115
|
};
|
|
16
|
-
|
|
116
|
+
const handleCountryClick = (code) => {
|
|
117
|
+
onChange(code);
|
|
118
|
+
setValue(name, code);
|
|
119
|
+
setOpen(false);
|
|
120
|
+
};
|
|
121
|
+
const handleSearchChange = (e) => {
|
|
122
|
+
e.stopPropagation();
|
|
123
|
+
setSearchText(e.target.value);
|
|
124
|
+
};
|
|
125
|
+
const handleKeyDown = (e) => {
|
|
126
|
+
e.stopPropagation();
|
|
127
|
+
if (e.key === "Escape") {
|
|
128
|
+
setOpen(false);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const handleNavigation = (direction) => {
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
setFocusedIndex((prev) => {
|
|
134
|
+
if (direction === "next") {
|
|
135
|
+
if (prev === -1)
|
|
136
|
+
return 0;
|
|
137
|
+
return prev >= filteredCountries.length - 1 ? 0 : prev + 1;
|
|
138
|
+
}
|
|
139
|
+
if (prev === -1)
|
|
140
|
+
return filteredCountries.length - 1;
|
|
141
|
+
return prev <= 0 ? filteredCountries.length - 1 : prev - 1;
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
if (e.key === "Tab") {
|
|
145
|
+
handleNavigation(e.shiftKey ? "prev" : "next");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (e.key === "ArrowDown") {
|
|
149
|
+
handleNavigation("next");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (e.key === "ArrowUp") {
|
|
153
|
+
handleNavigation("prev");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (e.key === "Enter" && focusedIndex >= 0 && focusedIndex < filteredCountries.length) {
|
|
157
|
+
e.preventDefault();
|
|
158
|
+
const country = parseCountry(filteredCountries[focusedIndex]);
|
|
159
|
+
handleCountryClick(country.iso2);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
return /* @__PURE__ */ jsxs(
|
|
17
163
|
Select,
|
|
18
164
|
{
|
|
19
165
|
ref,
|
|
166
|
+
open,
|
|
167
|
+
onOpen: () => setOpen(true),
|
|
168
|
+
onClose: () => setOpen(false),
|
|
20
169
|
MenuProps: {
|
|
21
170
|
style: {
|
|
22
|
-
|
|
171
|
+
maxHeight: "300px",
|
|
23
172
|
top: "10px"
|
|
24
173
|
},
|
|
25
174
|
anchorOrigin: {
|
|
@@ -29,6 +178,18 @@ const CountrySelect = forwardRef(({ value, onChange, name, sx }, ref) => {
|
|
|
29
178
|
transformOrigin: {
|
|
30
179
|
vertical: "top",
|
|
31
180
|
horizontal: "left"
|
|
181
|
+
},
|
|
182
|
+
PaperProps: {
|
|
183
|
+
ref: menuRef,
|
|
184
|
+
sx: {
|
|
185
|
+
display: "flex",
|
|
186
|
+
"& .MuiList-root": {
|
|
187
|
+
pt: 0,
|
|
188
|
+
display: "flex",
|
|
189
|
+
flexDirection: "column",
|
|
190
|
+
overflowY: "hidden"
|
|
191
|
+
}
|
|
192
|
+
}
|
|
32
193
|
}
|
|
33
194
|
},
|
|
34
195
|
sx: {
|
|
@@ -55,23 +216,84 @@ const CountrySelect = forwardRef(({ value, onChange, name, sx }, ref) => {
|
|
|
55
216
|
},
|
|
56
217
|
value,
|
|
57
218
|
onChange: onCountryChange,
|
|
58
|
-
renderValue: (code) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
219
|
+
renderValue: (code) => /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", flexWrap: "nowrap", gap: 0.5, sx: { cursor: "pointer" }, children: [
|
|
220
|
+
/* @__PURE__ */ jsx(FlagEmoji, { iso2: code, style: { display: "flex" } }),
|
|
221
|
+
/* @__PURE__ */ jsx(Typography, { children: countryDetail?.name })
|
|
222
|
+
] }),
|
|
223
|
+
children: [
|
|
224
|
+
/* @__PURE__ */ jsx(
|
|
225
|
+
Box,
|
|
226
|
+
{
|
|
227
|
+
sx: {
|
|
228
|
+
position: "sticky",
|
|
229
|
+
top: 0,
|
|
230
|
+
zIndex: 1,
|
|
231
|
+
bgcolor: "background.paper",
|
|
232
|
+
p: 1
|
|
233
|
+
},
|
|
234
|
+
onClick: (e) => {
|
|
235
|
+
e.stopPropagation();
|
|
236
|
+
},
|
|
237
|
+
children: /* @__PURE__ */ jsx(
|
|
238
|
+
TextField,
|
|
239
|
+
{
|
|
240
|
+
inputRef,
|
|
241
|
+
autoFocus: !isMobile,
|
|
242
|
+
fullWidth: true,
|
|
243
|
+
placeholder: "Search country...",
|
|
244
|
+
value: searchText,
|
|
245
|
+
onChange: handleSearchChange,
|
|
246
|
+
onKeyDown: handleKeyDown,
|
|
247
|
+
onClick: (e) => e.stopPropagation(),
|
|
248
|
+
size: "small",
|
|
249
|
+
variant: "outlined"
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
),
|
|
254
|
+
/* @__PURE__ */ jsx(
|
|
255
|
+
Box,
|
|
256
|
+
{
|
|
257
|
+
ref: listRef,
|
|
258
|
+
sx: {
|
|
259
|
+
flex: 1,
|
|
260
|
+
overflowY: "auto",
|
|
261
|
+
overflowX: "hidden",
|
|
262
|
+
maxHeight: "calc(300px - 65px)",
|
|
263
|
+
scrollBehavior: "smooth"
|
|
264
|
+
},
|
|
265
|
+
children: filteredCountries.length > 0 ? filteredCountries.map((c, index) => {
|
|
266
|
+
const parsed = parseCountry(c);
|
|
267
|
+
const isFocused = index === focusedIndex;
|
|
268
|
+
return /* @__PURE__ */ jsxs(
|
|
269
|
+
MenuItem,
|
|
270
|
+
{
|
|
271
|
+
value: parsed.iso2,
|
|
272
|
+
selected: parsed.iso2 === value,
|
|
273
|
+
onClick: () => handleCountryClick(parsed.iso2),
|
|
274
|
+
sx: {
|
|
275
|
+
"&.Mui-selected": {
|
|
276
|
+
backgroundColor: "rgba(0, 0, 0, 0.04)"
|
|
277
|
+
},
|
|
278
|
+
"&:hover": {
|
|
279
|
+
backgroundColor: "var(--backgrounds-bg-highlight, #eff6ff)"
|
|
280
|
+
},
|
|
281
|
+
...isFocused ? {
|
|
282
|
+
backgroundColor: "var(--backgrounds-bg-highlight, #eff6ff)",
|
|
283
|
+
outline: "none"
|
|
284
|
+
} : {}
|
|
285
|
+
},
|
|
286
|
+
children: [
|
|
287
|
+
/* @__PURE__ */ jsx(FlagEmoji, { iso2: parsed.iso2, style: { marginRight: "8px" } }),
|
|
288
|
+
/* @__PURE__ */ jsx(Typography, { children: parsed.name })
|
|
289
|
+
]
|
|
290
|
+
},
|
|
291
|
+
parsed.iso2
|
|
292
|
+
);
|
|
293
|
+
}) : /* @__PURE__ */ jsx(MenuItem, { disabled: true, children: /* @__PURE__ */ jsx(Typography, { color: "text.secondary", children: "No countries found" }) })
|
|
294
|
+
}
|
|
295
|
+
)
|
|
296
|
+
]
|
|
75
297
|
}
|
|
76
298
|
);
|
|
77
299
|
});
|
|
@@ -27,13 +27,14 @@ type Props = {
|
|
|
27
27
|
subscriptionCount?: number;
|
|
28
28
|
detailUrl: string;
|
|
29
29
|
}) => React.ReactNode;
|
|
30
|
+
authToken?: string;
|
|
30
31
|
};
|
|
31
32
|
type SummaryItem = {
|
|
32
33
|
amount: string;
|
|
33
34
|
currency: PaymentCurrency;
|
|
34
35
|
method: PaymentMethod;
|
|
35
36
|
};
|
|
36
|
-
declare function OverdueInvoicePayment({ subscriptionId, customerId, mode, dialogProps, children, onPaid, detailLinkOptions, successToast, alertMessage, }: Props): import("react").JSX.Element | null;
|
|
37
|
+
declare function OverdueInvoicePayment({ subscriptionId, customerId, mode, dialogProps, children, onPaid, detailLinkOptions, successToast, alertMessage, authToken, }: Props): import("react").JSX.Element | null;
|
|
37
38
|
declare namespace OverdueInvoicePayment {
|
|
38
39
|
var defaultProps: {
|
|
39
40
|
mode: string;
|
|
@@ -49,6 +50,7 @@ declare namespace OverdueInvoicePayment {
|
|
|
49
50
|
customerId: undefined;
|
|
50
51
|
successToast: boolean;
|
|
51
52
|
alertMessage: string;
|
|
53
|
+
authToken: undefined;
|
|
52
54
|
};
|
|
53
55
|
}
|
|
54
56
|
export default OverdueInvoicePayment;
|
|
@@ -23,7 +23,7 @@ const fetchOverdueInvoices = async (params) => {
|
|
|
23
23
|
} else {
|
|
24
24
|
url = `/api/customers/${params.customerId}/overdue/invoices`;
|
|
25
25
|
}
|
|
26
|
-
const res = await api.get(url);
|
|
26
|
+
const res = await api.get(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url);
|
|
27
27
|
return res.data;
|
|
28
28
|
};
|
|
29
29
|
function OverdueInvoicePayment({
|
|
@@ -36,7 +36,8 @@ function OverdueInvoicePayment({
|
|
|
36
36
|
},
|
|
37
37
|
detailLinkOptions = { enabled: true },
|
|
38
38
|
successToast = true,
|
|
39
|
-
alertMessage = ""
|
|
39
|
+
alertMessage = "",
|
|
40
|
+
authToken
|
|
40
41
|
}) {
|
|
41
42
|
const { t } = useLocaleContext();
|
|
42
43
|
const { connect } = usePaymentContext();
|
|
@@ -55,7 +56,7 @@ function OverdueInvoicePayment({
|
|
|
55
56
|
error,
|
|
56
57
|
loading,
|
|
57
58
|
runAsync: refresh
|
|
58
|
-
} = useRequest(() => fetchOverdueInvoices({ subscriptionId, customerId }), {
|
|
59
|
+
} = useRequest(() => fetchOverdueInvoices({ subscriptionId, customerId, authToken }), {
|
|
59
60
|
ready: !!subscriptionId || !!customerId
|
|
60
61
|
});
|
|
61
62
|
const detailUrl = useMemo(() => {
|
|
@@ -396,6 +397,7 @@ OverdueInvoicePayment.defaultProps = {
|
|
|
396
397
|
subscriptionId: void 0,
|
|
397
398
|
customerId: void 0,
|
|
398
399
|
successToast: true,
|
|
399
|
-
alertMessage: ""
|
|
400
|
+
alertMessage: "",
|
|
401
|
+
authToken: void 0
|
|
400
402
|
};
|
|
401
403
|
export default OverdueInvoicePayment;
|
package/es/contexts/payment.d.ts
CHANGED
|
@@ -25,10 +25,11 @@ export type PaymentContextProps = {
|
|
|
25
25
|
connect: import('@arcblock/did-connect/lib/types').SessionContext['connectApi'];
|
|
26
26
|
children: any;
|
|
27
27
|
baseUrl?: string;
|
|
28
|
+
authToken?: string;
|
|
28
29
|
};
|
|
29
30
|
declare const PaymentContext: import("react").Context<PaymentContextType>;
|
|
30
31
|
declare const Consumer: import("react").Consumer<PaymentContextType>;
|
|
31
|
-
declare function PaymentProvider({ session, connect, children, baseUrl }: PaymentContextProps): import("react").JSX.Element | null;
|
|
32
|
+
declare function PaymentProvider({ session, connect, children, baseUrl, authToken }: PaymentContextProps): import("react").JSX.Element | null;
|
|
32
33
|
declare namespace PaymentProvider {
|
|
33
34
|
var defaultProps: {};
|
|
34
35
|
}
|
package/es/contexts/payment.js
CHANGED
|
@@ -33,9 +33,16 @@ const getCurrency = (currencyId, methods) => {
|
|
|
33
33
|
const getMethod = (methodId, methods) => {
|
|
34
34
|
return methods.find((x) => x.id === methodId);
|
|
35
35
|
};
|
|
36
|
-
function PaymentProvider({ session, connect, children, baseUrl }) {
|
|
36
|
+
function PaymentProvider({ session, connect, children, baseUrl, authToken }) {
|
|
37
37
|
if (baseUrl) {
|
|
38
38
|
window.__PAYMENT_KIT_BASE_URL = baseUrl;
|
|
39
|
+
} else {
|
|
40
|
+
window.__PAYMENT_KIT_BASE_URL = "";
|
|
41
|
+
}
|
|
42
|
+
if (authToken) {
|
|
43
|
+
window.__PAYMENT_KIT_AUTH_TOKEN = authToken;
|
|
44
|
+
} else {
|
|
45
|
+
window.__PAYMENT_KIT_AUTH_TOKEN = "";
|
|
39
46
|
}
|
|
40
47
|
const [livemode, setLivemode] = useLocalStorageState("livemode", { defaultValue: true });
|
|
41
48
|
const {
|
package/es/hooks/keyboard.js
CHANGED
|
@@ -39,6 +39,9 @@ export const useTabNavigation = (items, onSelect, options) => {
|
|
|
39
39
|
return i;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
if (includeCustom && navigableElements.length > 0) {
|
|
43
|
+
return navigableElements.length - 1;
|
|
44
|
+
}
|
|
42
45
|
}
|
|
43
46
|
return -1;
|
|
44
47
|
}, [items, includeCustom, isCustomSelected, currentValue, valueType, compareValue, findNavigableElements]);
|
package/es/index.d.ts
CHANGED
|
@@ -43,5 +43,6 @@ export * from './hooks/mobile';
|
|
|
43
43
|
export * from './hooks/table';
|
|
44
44
|
export * from './hooks/scroll';
|
|
45
45
|
export * from './hooks/keyboard';
|
|
46
|
+
export * from './libs/validator';
|
|
46
47
|
export { translations, createTranslator } from './locales';
|
|
47
48
|
export { createLazyComponent, api, dayjs, FormInput, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, PaymentBeneficiaries, LoadingButton, DonateDetails, };
|
package/es/index.js
CHANGED
|
@@ -43,6 +43,7 @@ export * from "./hooks/mobile.js";
|
|
|
43
43
|
export * from "./hooks/table.js";
|
|
44
44
|
export * from "./hooks/scroll.js";
|
|
45
45
|
export * from "./hooks/keyboard.js";
|
|
46
|
+
export * from "./libs/validator.js";
|
|
46
47
|
export { translations, createTranslator } from "./locales/index.js";
|
|
47
48
|
export {
|
|
48
49
|
createLazyComponent,
|
package/es/libs/api.js
CHANGED
|
@@ -10,6 +10,10 @@ api.interceptors.request.use(
|
|
|
10
10
|
const locale = getLocale(window.blocklet?.languages);
|
|
11
11
|
const query = new URLSearchParams(config.url?.split("?").pop());
|
|
12
12
|
config.params = { ...config.params || {}, locale };
|
|
13
|
+
const authToken = window.__PAYMENT_KIT_AUTH_TOKEN;
|
|
14
|
+
if (authToken && typeof config.params.authToken === "undefined" && !query.has("authToken")) {
|
|
15
|
+
config.params.authToken = authToken;
|
|
16
|
+
}
|
|
13
17
|
if (typeof config.params.livemode === "undefined" && query.has("livemode") === false) {
|
|
14
18
|
const livemode = localStorage.getItem("livemode");
|
|
15
19
|
config.params.livemode = isNull(livemode) ? true : JSON.parse(livemode);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const getUserStorageKey: (base: string, did?: string) => string;
|
|
2
|
+
export declare const saveCurrencyPreference: (currencyId: string, did?: string) => void;
|
|
3
|
+
export declare const getCurrencyPreference: (did?: string, availableCurrencyIds?: string[]) => string | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const CURRENCY_PREFERENCE_KEY_BASE = "payment-currency-preference";
|
|
2
|
+
export const getUserStorageKey = (base, did) => {
|
|
3
|
+
return did ? `${base}:${did}` : base;
|
|
4
|
+
};
|
|
5
|
+
export const saveCurrencyPreference = (currencyId, did) => {
|
|
6
|
+
try {
|
|
7
|
+
localStorage.setItem(getUserStorageKey(CURRENCY_PREFERENCE_KEY_BASE, did), currencyId);
|
|
8
|
+
} catch (e) {
|
|
9
|
+
console.warn("Failed to save currency preference", e);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
export const getCurrencyPreference = (did, availableCurrencyIds) => {
|
|
13
|
+
try {
|
|
14
|
+
const saved = localStorage.getItem(getUserStorageKey(CURRENCY_PREFERENCE_KEY_BASE, did));
|
|
15
|
+
if (saved && (!availableCurrencyIds || availableCurrencyIds.includes(saved))) {
|
|
16
|
+
return saved;
|
|
17
|
+
}
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.warn("Failed to access localStorage", e);
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
};
|
package/es/libs/util.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export declare function formatDateTime(date: Date | string | number, locale?: st
|
|
|
10
10
|
export declare const formatLocale: (locale?: string) => string;
|
|
11
11
|
export declare const formatPrettyMsLocale: (locale: string) => "zh_CN" | "en_US";
|
|
12
12
|
export declare const formatError: (err: any) => any;
|
|
13
|
-
export declare function formatBNStr(str?: string, decimals?: number, precision?: number, trim?: boolean): string;
|
|
14
|
-
export declare function formatNumber(n: number | string, precision?: number, trim?: boolean): string;
|
|
13
|
+
export declare function formatBNStr(str?: string, decimals?: number, precision?: number, trim?: boolean, thousandSeparated?: boolean): string;
|
|
14
|
+
export declare function formatNumber(n: number | string, precision?: number, trim?: boolean, thousandSeparated?: boolean): string;
|
|
15
15
|
export declare const formatPrice: (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity?: number, bn?: boolean, locale?: string) => string;
|
|
16
16
|
export declare const formatPriceAmount: (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity?: number, bn?: boolean) => string;
|
|
17
17
|
export declare function getStatementDescriptor(items: any[]): any;
|
package/es/libs/util.js
CHANGED
|
@@ -85,16 +85,19 @@ export const formatError = (err) => {
|
|
|
85
85
|
}
|
|
86
86
|
return err.message;
|
|
87
87
|
};
|
|
88
|
-
export function formatBNStr(str = "", decimals = 18, precision = 6, trim = true) {
|
|
89
|
-
|
|
88
|
+
export function formatBNStr(str = "", decimals = 18, precision = 6, trim = true, thousandSeparated = true) {
|
|
89
|
+
if (!str) {
|
|
90
|
+
return "0";
|
|
91
|
+
}
|
|
92
|
+
return formatNumber(fromUnitToToken(str, decimals), precision, trim, thousandSeparated);
|
|
90
93
|
}
|
|
91
|
-
export function formatNumber(n, precision = 6, trim = true) {
|
|
94
|
+
export function formatNumber(n, precision = 6, trim = true, thousandSeparated = true) {
|
|
92
95
|
if (!n || n === "0") {
|
|
93
96
|
return "0";
|
|
94
97
|
}
|
|
95
98
|
const num = numbro(n);
|
|
96
99
|
const options = {
|
|
97
|
-
thousandSeparated
|
|
100
|
+
thousandSeparated,
|
|
98
101
|
...(precision || precision === 0) && { mantissa: precision }
|
|
99
102
|
};
|
|
100
103
|
const result = num.format(options);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function validatePostalCode(postalCode: string, country?: string): boolean;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import isPostalCode from "validator/lib/isPostalCode";
|
|
2
|
+
const POSTAL_CODE_SUPPORTED_COUNTRIES = [
|
|
3
|
+
"AD",
|
|
4
|
+
"AT",
|
|
5
|
+
"AU",
|
|
6
|
+
"BE",
|
|
7
|
+
"BG",
|
|
8
|
+
"BR",
|
|
9
|
+
"CA",
|
|
10
|
+
"CH",
|
|
11
|
+
"CN",
|
|
12
|
+
"CZ",
|
|
13
|
+
"DE",
|
|
14
|
+
"DK",
|
|
15
|
+
"DZ",
|
|
16
|
+
"EE",
|
|
17
|
+
"ES",
|
|
18
|
+
"FI",
|
|
19
|
+
"FR",
|
|
20
|
+
"GB",
|
|
21
|
+
"GR",
|
|
22
|
+
"HR",
|
|
23
|
+
"HU",
|
|
24
|
+
"ID",
|
|
25
|
+
"IE",
|
|
26
|
+
"IL",
|
|
27
|
+
"IN",
|
|
28
|
+
"IR",
|
|
29
|
+
"IS",
|
|
30
|
+
"IT",
|
|
31
|
+
"JP",
|
|
32
|
+
"KE",
|
|
33
|
+
"KR",
|
|
34
|
+
"LI",
|
|
35
|
+
"LT",
|
|
36
|
+
"LU",
|
|
37
|
+
"LV",
|
|
38
|
+
"MX",
|
|
39
|
+
"MT",
|
|
40
|
+
"NL",
|
|
41
|
+
"NO",
|
|
42
|
+
"NZ",
|
|
43
|
+
"PL",
|
|
44
|
+
"PR",
|
|
45
|
+
"PT",
|
|
46
|
+
"RO",
|
|
47
|
+
"RU",
|
|
48
|
+
"SA",
|
|
49
|
+
"SE",
|
|
50
|
+
"SI",
|
|
51
|
+
"SK",
|
|
52
|
+
"TN",
|
|
53
|
+
"TW",
|
|
54
|
+
"UA",
|
|
55
|
+
"US",
|
|
56
|
+
"ZA",
|
|
57
|
+
"ZM"
|
|
58
|
+
];
|
|
59
|
+
export function validatePostalCode(postalCode, country) {
|
|
60
|
+
if (!postalCode)
|
|
61
|
+
return true;
|
|
62
|
+
const countryUpper = country?.toUpperCase();
|
|
63
|
+
const isSupported = country && POSTAL_CODE_SUPPORTED_COUNTRIES.includes(countryUpper);
|
|
64
|
+
try {
|
|
65
|
+
return isPostalCode(postalCode, isSupported ? countryUpper : "any");
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(error);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
3
3
|
import { Fade, FormLabel, InputAdornment, Stack } from "@mui/material";
|
|
4
|
-
import { Controller, useFormContext } from "react-hook-form";
|
|
4
|
+
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
|
5
5
|
import FormInput from "../../components/input.js";
|
|
6
6
|
import CountrySelect from "../../components/country-select.js";
|
|
7
|
+
import { validatePostalCode } from "../../libs/validator.js";
|
|
7
8
|
AddressForm.defaultProps = {
|
|
8
9
|
sx: {}
|
|
9
10
|
};
|
|
10
11
|
export default function AddressForm({ mode, stripe, sx = {} }) {
|
|
11
12
|
const { t } = useLocaleContext();
|
|
12
13
|
const { control } = useFormContext();
|
|
14
|
+
const country = useWatch({ control, name: "billing_address.country" });
|
|
13
15
|
if (mode === "required") {
|
|
14
16
|
return /* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsx(Stack, { className: "cko-payment-address cko-payment-form", sx, children: /* @__PURE__ */ jsxs(Stack, { direction: "column", className: "cko-payment-form", spacing: 0, children: [
|
|
15
17
|
/* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.billing.line1") }),
|
|
@@ -50,7 +52,13 @@ export default function AddressForm({ mode, stripe, sx = {} }) {
|
|
|
50
52
|
FormInput,
|
|
51
53
|
{
|
|
52
54
|
name: "billing_address.postal_code",
|
|
53
|
-
rules: {
|
|
55
|
+
rules: {
|
|
56
|
+
required: t("payment.checkout.required"),
|
|
57
|
+
validate: (x) => {
|
|
58
|
+
const isValid = validatePostalCode(x, country);
|
|
59
|
+
return isValid ? true : t("payment.checkout.invalid");
|
|
60
|
+
}
|
|
61
|
+
},
|
|
54
62
|
errorPosition: "right",
|
|
55
63
|
variant: "outlined",
|
|
56
64
|
placeholder: t("payment.checkout.billing.postal_code"),
|
|
@@ -85,7 +93,13 @@ export default function AddressForm({ mode, stripe, sx = {} }) {
|
|
|
85
93
|
FormInput,
|
|
86
94
|
{
|
|
87
95
|
name: "billing_address.postal_code",
|
|
88
|
-
rules: {
|
|
96
|
+
rules: {
|
|
97
|
+
required: t("payment.checkout.required"),
|
|
98
|
+
validate: (x) => {
|
|
99
|
+
const isValid = validatePostalCode(x, country);
|
|
100
|
+
return isValid ? true : t("payment.checkout.invalid");
|
|
101
|
+
}
|
|
102
|
+
},
|
|
89
103
|
errorPosition: "right",
|
|
90
104
|
variant: "outlined",
|
|
91
105
|
placeholder: t("payment.checkout.billing.postal_code"),
|