@blocklet/payment-react 1.14.28 → 1.14.30
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/components/livemode.js +1 -1
- package/es/components/table.js +1 -1
- package/es/history/invoice/list.js +12 -10
- package/es/libs/util.d.ts +1 -0
- package/es/libs/util.js +7 -0
- package/es/locales/en.js +3 -2
- package/es/locales/zh.js +3 -2
- package/es/payment/form/index.js +89 -60
- package/es/payment/index.js +81 -15
- package/es/payment/product-card.js +2 -2
- package/lib/components/livemode.js +1 -1
- package/lib/components/table.js +1 -1
- package/lib/history/invoice/list.js +10 -8
- package/lib/libs/util.d.ts +1 -0
- package/lib/libs/util.js +8 -0
- package/lib/locales/en.js +3 -2
- package/lib/locales/zh.js +3 -2
- package/lib/payment/form/index.js +37 -11
- package/lib/payment/index.js +59 -4
- package/lib/payment/product-card.js +2 -2
- package/package.json +3 -3
- package/src/components/livemode.tsx +1 -1
- package/src/components/table.tsx +1 -1
- package/src/history/invoice/list.tsx +11 -10
- package/src/libs/util.ts +8 -0
- package/src/locales/en.tsx +3 -1
- package/src/locales/zh.tsx +2 -1
- package/src/payment/form/index.tsx +43 -15
- package/src/payment/index.tsx +70 -5
- package/src/payment/product-card.tsx +2 -2
package/es/components/table.js
CHANGED
|
@@ -83,7 +83,7 @@ const Wrapped = styled(Datatable)`
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
tr.MuiTableRow-root:not(.MuiTableRow-footer):hover {
|
|
86
|
-
background: #
|
|
86
|
+
background: var(--backgrounds-bg-highlight, #eff6ff);
|
|
87
87
|
}
|
|
88
88
|
tr.MuiTableRow-root:last-of-type td:first-of-type {
|
|
89
89
|
border-bottom-left-radius: 8px;
|
|
@@ -94,28 +94,30 @@ const InvoiceTable = React.memo((props) => {
|
|
|
94
94
|
);
|
|
95
95
|
const columns = [
|
|
96
96
|
{
|
|
97
|
-
label: t("
|
|
98
|
-
name: "
|
|
97
|
+
label: t("common.amount"),
|
|
98
|
+
name: "total",
|
|
99
|
+
width: 60,
|
|
100
|
+
align: "right",
|
|
99
101
|
options: {
|
|
100
102
|
customBodyRenderLite: (_, index) => {
|
|
101
103
|
const invoice = data?.list[index];
|
|
102
104
|
const link = getInvoiceLink(invoice, action);
|
|
103
|
-
return /* @__PURE__ */ jsx("a", { href: link.url, target: link.external ? "_blank" : target, rel: "noreferrer", children:
|
|
105
|
+
return /* @__PURE__ */ jsx("a", { href: link.url, target: link.external ? "_blank" : target, rel: "noreferrer", children: /* @__PURE__ */ jsxs(Typography, { children: [
|
|
106
|
+
formatBNStr(invoice.total, invoice.paymentCurrency.decimal),
|
|
107
|
+
"\xA0",
|
|
108
|
+
invoice.paymentCurrency.symbol
|
|
109
|
+
] }) });
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
},
|
|
107
113
|
{
|
|
108
|
-
label: t("
|
|
109
|
-
name: "
|
|
114
|
+
label: t("payment.customer.invoice.invoiceNumber"),
|
|
115
|
+
name: "number",
|
|
110
116
|
options: {
|
|
111
117
|
customBodyRenderLite: (_, index) => {
|
|
112
118
|
const invoice = data?.list[index];
|
|
113
119
|
const link = getInvoiceLink(invoice, action);
|
|
114
|
-
return /* @__PURE__ */ jsx("a", { href: link.url, target: link.external ? "_blank" : target, rel: "noreferrer", children:
|
|
115
|
-
formatBNStr(invoice.total, invoice.paymentCurrency.decimal),
|
|
116
|
-
"\xA0",
|
|
117
|
-
invoice.paymentCurrency.symbol
|
|
118
|
-
] }) });
|
|
120
|
+
return /* @__PURE__ */ jsx("a", { href: link.url, target: link.external ? "_blank" : target, rel: "noreferrer", children: invoice?.number });
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
},
|
package/es/libs/util.d.ts
CHANGED
|
@@ -105,3 +105,4 @@ export declare function formatQuantityInventory(price: TPrice, quantity: string
|
|
|
105
105
|
export declare function formatSubscriptionStatus(status: string): string;
|
|
106
106
|
export declare function formatAmountPrecisionLimit(amount: string, locale?: string, precision?: number): string;
|
|
107
107
|
export declare function getWordBreakStyle(value: any): 'break-word' | 'break-all';
|
|
108
|
+
export declare function isMobileSafari(): boolean;
|
package/es/libs/util.js
CHANGED
|
@@ -805,3 +805,10 @@ export function getWordBreakStyle(value) {
|
|
|
805
805
|
}
|
|
806
806
|
return "break-all";
|
|
807
807
|
}
|
|
808
|
+
export function isMobileSafari() {
|
|
809
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
810
|
+
const isSafari = ua.indexOf("safari") > -1 && ua.indexOf("chrome") === -1;
|
|
811
|
+
const isMobile = ua.indexOf("mobile") > -1 || /iphone|ipad|ipod/.test(ua);
|
|
812
|
+
const isIOS = /iphone|ipad|ipod/.test(ua);
|
|
813
|
+
return isSafari && isMobile && isIOS;
|
|
814
|
+
}
|
package/es/locales/en.js
CHANGED
|
@@ -87,7 +87,8 @@ export default flat({
|
|
|
87
87
|
recoverFrom: "Recovered From",
|
|
88
88
|
quantityLimitPerCheckout: "Exceed purchase limit",
|
|
89
89
|
quantityNotEnough: "Exceed inventory",
|
|
90
|
-
amountPrecisionLimit: "Amount decimal places must be less than or equal to {precision}"
|
|
90
|
+
amountPrecisionLimit: "Amount decimal places must be less than or equal to {precision}",
|
|
91
|
+
saveAsDefaultPriceSuccess: "Set default price successfully"
|
|
91
92
|
},
|
|
92
93
|
payment: {
|
|
93
94
|
checkout: {
|
|
@@ -226,7 +227,7 @@ export default flat({
|
|
|
226
227
|
recover: {
|
|
227
228
|
button: "Renew",
|
|
228
229
|
title: "Renew your subscription",
|
|
229
|
-
description: "Your subscription will
|
|
230
|
+
description: "Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue"
|
|
230
231
|
},
|
|
231
232
|
changePlan: {
|
|
232
233
|
button: "Update",
|
package/es/locales/zh.js
CHANGED
|
@@ -87,7 +87,8 @@ export default flat({
|
|
|
87
87
|
recoverFrom: "\u6062\u590D\u81EA",
|
|
88
88
|
quantityLimitPerCheckout: "\u8D85\u51FA\u8D2D\u4E70\u9650\u5236",
|
|
89
89
|
quantityNotEnough: "\u5E93\u5B58\u4E0D\u8DB3",
|
|
90
|
-
amountPrecisionLimit: "\u91D1\u989D\u5C0F\u6570\u4F4D\u6570\u5FC5\u987B\u5728 {precision} \u4F4D\u4EE5\u5185"
|
|
90
|
+
amountPrecisionLimit: "\u91D1\u989D\u5C0F\u6570\u4F4D\u6570\u5FC5\u987B\u5728 {precision} \u4F4D\u4EE5\u5185",
|
|
91
|
+
saveAsDefaultPriceSuccess: "\u8BBE\u7F6E\u9ED8\u8BA4\u4EF7\u683C\u6210\u529F"
|
|
91
92
|
},
|
|
92
93
|
payment: {
|
|
93
94
|
checkout: {
|
|
@@ -226,7 +227,7 @@ export default flat({
|
|
|
226
227
|
recover: {
|
|
227
228
|
button: "\u7EED\u8BA2",
|
|
228
229
|
title: "\u7EED\u8BA2\u60A8\u7684\u8BA2\u9605",
|
|
229
|
-
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u4E0D\
|
|
230
|
+
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u4E0D\u4F1A\u88AB\u53D6\u6D88\uFF0C\u5E76\u5C06\u5728{date}\u81EA\u52A8\u7EED\u8BA2\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u7EE7\u7EED"
|
|
230
231
|
},
|
|
231
232
|
changePlan: {
|
|
232
233
|
button: "\u66F4\u65B0",
|
package/es/payment/form/index.js
CHANGED
|
@@ -3,15 +3,16 @@ import "react-international-phone/style.css";
|
|
|
3
3
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
4
4
|
import Toast from "@arcblock/ux/lib/Toast";
|
|
5
5
|
import { LoadingButton } from "@mui/lab";
|
|
6
|
-
import { Divider, Fade, FormLabel, Stack, Typography } from "@mui/material";
|
|
6
|
+
import { Box, Divider, Fade, FormLabel, Stack, Typography } from "@mui/material";
|
|
7
7
|
import { useMemoizedFn, useSetState } from "ahooks";
|
|
8
8
|
import { PhoneNumberUtil } from "google-libphonenumber";
|
|
9
9
|
import pWaitFor from "p-wait-for";
|
|
10
|
-
import { useEffect, useMemo, useState } from "react";
|
|
10
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
11
11
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
|
12
12
|
import { joinURL } from "ufo";
|
|
13
13
|
import { dispatch } from "use-bus";
|
|
14
14
|
import isEmail from "validator/es/lib/isEmail";
|
|
15
|
+
import { isEmpty } from "lodash";
|
|
15
16
|
import ConfirmDialog from "../../components/confirm.js";
|
|
16
17
|
import FormInput from "../../components/input.js";
|
|
17
18
|
import { usePaymentContext } from "../../contexts/payment.js";
|
|
@@ -29,6 +30,7 @@ import AddressForm from "./address.js";
|
|
|
29
30
|
import CurrencySelector from "./currency.js";
|
|
30
31
|
import PhoneInput from "./phone.js";
|
|
31
32
|
import StripeCheckout from "./stripe.js";
|
|
33
|
+
import { useMobile } from "../../hooks/mobile.js";
|
|
32
34
|
const phoneUtil = PhoneNumberUtil.getInstance();
|
|
33
35
|
const waitForCheckoutComplete = async (sessionId) => {
|
|
34
36
|
let result;
|
|
@@ -62,9 +64,17 @@ export default function PaymentForm({
|
|
|
62
64
|
action
|
|
63
65
|
}) {
|
|
64
66
|
const { t } = useLocaleContext();
|
|
67
|
+
const { isMobile } = useMobile();
|
|
65
68
|
const { session, connect, payable } = usePaymentContext();
|
|
66
69
|
const subscription = useSubscription("events");
|
|
67
|
-
const {
|
|
70
|
+
const {
|
|
71
|
+
control,
|
|
72
|
+
getValues,
|
|
73
|
+
setValue,
|
|
74
|
+
handleSubmit,
|
|
75
|
+
formState: { errors }
|
|
76
|
+
} = useFormContext();
|
|
77
|
+
const errorRef = useRef(null);
|
|
68
78
|
const quantityInventoryStatus = useMemo(() => {
|
|
69
79
|
let status = true;
|
|
70
80
|
for (const item of checkoutSession.line_items) {
|
|
@@ -163,6 +173,11 @@ export default function PaymentForm({
|
|
|
163
173
|
setState({ paying: false });
|
|
164
174
|
}
|
|
165
175
|
};
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
if (errorRef.current && !isEmpty(errors) && isMobile) {
|
|
178
|
+
errorRef.current.scrollIntoView({ behavior: "smooth" });
|
|
179
|
+
}
|
|
180
|
+
}, [errors, isMobile]);
|
|
166
181
|
const onUserLoggedIn = async () => {
|
|
167
182
|
const { data: profile } = await api.get("/api/customers/me?fallback=1");
|
|
168
183
|
if (profile) {
|
|
@@ -267,6 +282,9 @@ export default function PaymentForm({
|
|
|
267
282
|
setState({ submitting: false });
|
|
268
283
|
};
|
|
269
284
|
const onAction = () => {
|
|
285
|
+
if (errorRef.current && !isEmpty(errors) && isMobile) {
|
|
286
|
+
errorRef.current.scrollIntoView({ behavior: "smooth" });
|
|
287
|
+
}
|
|
270
288
|
if (session?.user) {
|
|
271
289
|
if (hasDidWallet(session.user)) {
|
|
272
290
|
handleSubmit(onFormSubmit, onFormError)();
|
|
@@ -343,68 +361,79 @@ export default function PaymentForm({
|
|
|
343
361
|
)
|
|
344
362
|
] }) }),
|
|
345
363
|
/* @__PURE__ */ jsx(Stack, { direction: "row", sx: { mb: 1 }, alignItems: "center", justifyContent: "space-between" }),
|
|
346
|
-
/* @__PURE__ */ jsxs(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
364
|
+
/* @__PURE__ */ jsxs(
|
|
365
|
+
Stack,
|
|
366
|
+
{
|
|
367
|
+
direction: "column",
|
|
368
|
+
className: "cko-payment-form",
|
|
369
|
+
id: "cko-payment-form",
|
|
370
|
+
spacing: 0,
|
|
371
|
+
ref: !isEmpty(errors) ? errorRef : void 0,
|
|
372
|
+
sx: { flex: 1, overflow: "auto" },
|
|
373
|
+
children: [
|
|
374
|
+
/* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.name") }),
|
|
375
|
+
/* @__PURE__ */ jsx(
|
|
376
|
+
FormInput,
|
|
377
|
+
{
|
|
378
|
+
name: "customer_name",
|
|
379
|
+
variant: "outlined",
|
|
380
|
+
errorPosition: "right",
|
|
381
|
+
rules: {
|
|
382
|
+
required: t("payment.checkout.required")
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
),
|
|
386
|
+
/* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.email") }),
|
|
387
|
+
/* @__PURE__ */ jsx(
|
|
388
|
+
FormInput,
|
|
389
|
+
{
|
|
390
|
+
name: "customer_email",
|
|
391
|
+
variant: "outlined",
|
|
392
|
+
errorPosition: "right",
|
|
393
|
+
rules: {
|
|
394
|
+
required: t("payment.checkout.required"),
|
|
395
|
+
validate: (x) => isEmail(x) ? true : t("payment.checkout.invalid")
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
),
|
|
399
|
+
checkoutSession.phone_number_collection?.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
400
|
+
/* @__PURE__ */ jsx(FormLabel, { className: "base-label", children: t("payment.checkout.customer.phone") }),
|
|
401
|
+
/* @__PURE__ */ jsx(
|
|
402
|
+
PhoneInput,
|
|
403
|
+
{
|
|
404
|
+
name: "customer_phone",
|
|
405
|
+
variant: "outlined",
|
|
406
|
+
errorPosition: "right",
|
|
407
|
+
placeholder: "Phone number",
|
|
408
|
+
rules: {
|
|
409
|
+
required: t("payment.checkout.required"),
|
|
410
|
+
validate: (x) => {
|
|
411
|
+
try {
|
|
412
|
+
const parsed = phoneUtil.parseAndKeepRawInput(x);
|
|
413
|
+
return phoneUtil.isValidNumber(parsed) ? true : t("payment.checkout.invalid");
|
|
414
|
+
} catch {
|
|
415
|
+
return t("payment.checkout.invalid");
|
|
416
|
+
}
|
|
417
|
+
}
|
|
389
418
|
}
|
|
390
419
|
}
|
|
420
|
+
)
|
|
421
|
+
] }),
|
|
422
|
+
/* @__PURE__ */ jsx(
|
|
423
|
+
AddressForm,
|
|
424
|
+
{
|
|
425
|
+
mode: checkoutSession.billing_address_collection,
|
|
426
|
+
stripe: method?.type === "stripe",
|
|
427
|
+
sx: { marginTop: "0 !important" }
|
|
391
428
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
AddressForm,
|
|
397
|
-
{
|
|
398
|
-
mode: checkoutSession.billing_address_collection,
|
|
399
|
-
stripe: method?.type === "stripe",
|
|
400
|
-
sx: { marginTop: "0 !important" }
|
|
401
|
-
}
|
|
402
|
-
)
|
|
403
|
-
] })
|
|
429
|
+
)
|
|
430
|
+
]
|
|
431
|
+
}
|
|
432
|
+
)
|
|
404
433
|
] }) }),
|
|
405
434
|
/* @__PURE__ */ jsx(Divider, { sx: { mt: 2.5, mb: 2.5 } }),
|
|
406
435
|
/* @__PURE__ */ jsx(Fade, { in: true, children: /* @__PURE__ */ jsxs(Stack, { className: "cko-payment-submit", children: [
|
|
407
|
-
/* @__PURE__ */ jsx(
|
|
436
|
+
/* @__PURE__ */ jsx(Box, { className: "cko-payment-submit-btn", children: /* @__PURE__ */ jsx(
|
|
408
437
|
LoadingButton,
|
|
409
438
|
{
|
|
410
439
|
variant: "contained",
|
|
@@ -417,7 +446,7 @@ export default function PaymentForm({
|
|
|
417
446
|
loading: state.submitting || state.paying,
|
|
418
447
|
children: state.submitting || state.paying ? t("payment.checkout.processing") : buttonText
|
|
419
448
|
}
|
|
420
|
-
),
|
|
449
|
+
) }),
|
|
421
450
|
["subscription", "setup"].includes(checkoutSession.mode) && /* @__PURE__ */ jsx(Typography, { sx: { mt: 2.5, color: "text.lighter", fontSize: "0.9rem", lineHeight: "1.1rem" }, children: t("payment.checkout.confirm", { payee }) })
|
|
422
451
|
] }) }),
|
|
423
452
|
state.customerLimited && /* @__PURE__ */ jsx(
|
package/es/payment/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { useEffect, useState } from "react";
|
|
|
11
11
|
import { FormProvider, useForm, useWatch } from "react-hook-form";
|
|
12
12
|
import { usePaymentContext } from "../contexts/payment.js";
|
|
13
13
|
import api from "../libs/api.js";
|
|
14
|
-
import { findCurrency, formatError, getStatementDescriptor, isValidCountry } from "../libs/util.js";
|
|
14
|
+
import { findCurrency, formatError, getStatementDescriptor, isMobileSafari, isValidCountry } from "../libs/util.js";
|
|
15
15
|
import PaymentError from "./error.js";
|
|
16
16
|
import CheckoutFooter from "./footer.js";
|
|
17
17
|
import PaymentForm from "./form/index.js";
|
|
@@ -66,6 +66,25 @@ function PaymentInner({
|
|
|
66
66
|
)
|
|
67
67
|
}
|
|
68
68
|
});
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!isMobileSafari()) {
|
|
71
|
+
return () => {
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
let scrollTop = 0;
|
|
75
|
+
const focusinHandler = () => {
|
|
76
|
+
scrollTop = window.scrollY;
|
|
77
|
+
};
|
|
78
|
+
const focusoutHandler = () => {
|
|
79
|
+
window.scrollTo(0, scrollTop);
|
|
80
|
+
};
|
|
81
|
+
document.body.addEventListener("focusin", focusinHandler);
|
|
82
|
+
document.body.addEventListener("focusout", focusoutHandler);
|
|
83
|
+
return () => {
|
|
84
|
+
document.body.removeEventListener("focusin", focusinHandler);
|
|
85
|
+
document.body.removeEventListener("focusout", focusoutHandler);
|
|
86
|
+
};
|
|
87
|
+
}, []);
|
|
69
88
|
const currencyId = useWatch({ control: methods.control, name: "payment_currency", defaultValue: defaultCurrencyId });
|
|
70
89
|
const currency = findCurrency(paymentMethods, currencyId) || settings.baseCurrency;
|
|
71
90
|
const method = paymentMethods.find((x) => x.id === currency.payment_method_id);
|
|
@@ -221,7 +240,9 @@ export default function Payment({
|
|
|
221
240
|
const { t } = useLocaleContext();
|
|
222
241
|
const { refresh, livemode, setLivemode } = usePaymentContext();
|
|
223
242
|
const [delay, setDelay] = useState(false);
|
|
243
|
+
const { isMobile } = useMobile();
|
|
224
244
|
const hideSummaryCard = mode.endsWith("-minimal") || !showCheckoutSummary;
|
|
245
|
+
const isMobileSafariEnv = isMobileSafari();
|
|
225
246
|
useEffect(() => {
|
|
226
247
|
setTimeout(() => {
|
|
227
248
|
setDelay(true);
|
|
@@ -291,7 +312,10 @@ export default function Payment({
|
|
|
291
312
|
{
|
|
292
313
|
display: "flex",
|
|
293
314
|
flexDirection: "column",
|
|
294
|
-
sx: {
|
|
315
|
+
sx: {
|
|
316
|
+
height: mode === "standalone" ? "100vh" : "auto",
|
|
317
|
+
overflow: isMobileSafariEnv ? "visible" : "hidden"
|
|
318
|
+
},
|
|
295
319
|
children: [
|
|
296
320
|
mode === "standalone" ? /* @__PURE__ */ jsx(
|
|
297
321
|
Header,
|
|
@@ -306,17 +330,58 @@ export default function Payment({
|
|
|
306
330
|
sx: { borderBottom: "1px solid var(--stroke-border-base, #EFF1F5)" }
|
|
307
331
|
}
|
|
308
332
|
) : null,
|
|
309
|
-
/* @__PURE__ */ jsxs(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
333
|
+
/* @__PURE__ */ jsxs(
|
|
334
|
+
Root,
|
|
335
|
+
{
|
|
336
|
+
mode,
|
|
337
|
+
sx: {
|
|
338
|
+
flex: 1,
|
|
339
|
+
overflow: {
|
|
340
|
+
xs: isMobileSafariEnv ? "visible" : "auto",
|
|
341
|
+
md: "hidden"
|
|
342
|
+
},
|
|
343
|
+
...isMobile && mode === "standalone" ? {
|
|
344
|
+
".cko-payment-submit-btn": {
|
|
345
|
+
position: "fixed",
|
|
346
|
+
bottom: 20,
|
|
347
|
+
left: 0,
|
|
348
|
+
right: 0,
|
|
349
|
+
zIndex: 999,
|
|
350
|
+
background: "#fff",
|
|
351
|
+
padding: "10px",
|
|
352
|
+
textAlign: "center",
|
|
353
|
+
button: {
|
|
354
|
+
color: "#fff",
|
|
355
|
+
maxWidth: 542
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
".cko-footer": {
|
|
359
|
+
position: "fixed",
|
|
360
|
+
bottom: 0,
|
|
361
|
+
left: 0,
|
|
362
|
+
right: 0,
|
|
363
|
+
zIndex: 999,
|
|
364
|
+
background: "#fff",
|
|
365
|
+
marginBottom: 0
|
|
366
|
+
},
|
|
367
|
+
".cko-payment": {
|
|
368
|
+
paddingBottom: "100px"
|
|
369
|
+
}
|
|
370
|
+
} : {}
|
|
371
|
+
},
|
|
372
|
+
children: [
|
|
373
|
+
goBack && /* @__PURE__ */ jsx(
|
|
374
|
+
ArrowBackOutlined,
|
|
375
|
+
{
|
|
376
|
+
sx: { mr: 0.5, color: "text.secondary", alignSelf: "flex-start", margin: "16px 0", cursor: "pointer" },
|
|
377
|
+
onClick: goBack,
|
|
378
|
+
fontSize: "medium"
|
|
379
|
+
}
|
|
380
|
+
),
|
|
381
|
+
/* @__PURE__ */ jsx(Stack, { className: "cko-container", sx: { gap: { sm: mode === "standalone" ? 0 : mode === "inline" ? 4 : 8 } }, children: renderContent() })
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
)
|
|
320
385
|
]
|
|
321
386
|
}
|
|
322
387
|
);
|
|
@@ -461,7 +526,6 @@ export const Root = styled(Box)`
|
|
|
461
526
|
}
|
|
462
527
|
|
|
463
528
|
@media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
|
|
464
|
-
background: ${(props) => props.mode === "standalone" ? "var(--backgrounds-bg-subtle, #F9FAFB)" : "transparent"};
|
|
465
529
|
padding-top: 0;
|
|
466
530
|
overflow: auto;
|
|
467
531
|
&:before {
|
|
@@ -470,7 +534,8 @@ export const Root = styled(Box)`
|
|
|
470
534
|
.cko-container {
|
|
471
535
|
flex-direction: column;
|
|
472
536
|
align-items: center;
|
|
473
|
-
|
|
537
|
+
justify-content: flex-start;
|
|
538
|
+
gap: 0;
|
|
474
539
|
overflow: visible;
|
|
475
540
|
min-width: 200px;
|
|
476
541
|
}
|
|
@@ -483,6 +548,7 @@ export const Root = styled(Box)`
|
|
|
483
548
|
width: 100%;
|
|
484
549
|
height: fit-content;
|
|
485
550
|
flex: none;
|
|
551
|
+
border-top: 1px solid var(--stroke-border-base, #eff1f5);
|
|
486
552
|
&:before {
|
|
487
553
|
display: none;
|
|
488
554
|
}
|
|
@@ -24,7 +24,7 @@ export default function ProductCard({ size, variant, name, logo, description, ex
|
|
|
24
24
|
className: "cko-ellipsis",
|
|
25
25
|
variant: "body1",
|
|
26
26
|
title: name,
|
|
27
|
-
sx: { fontWeight: 500, mb: 0.5, lineHeight: 1, fontSize: 16 },
|
|
27
|
+
sx: { fontWeight: 500, mb: 0.5, lineHeight: 1.2, fontSize: 16 },
|
|
28
28
|
color: "text.primary",
|
|
29
29
|
children: name
|
|
30
30
|
}
|
|
@@ -34,7 +34,7 @@ export default function ProductCard({ size, variant, name, logo, description, ex
|
|
|
34
34
|
{
|
|
35
35
|
variant: "body1",
|
|
36
36
|
title: description,
|
|
37
|
-
sx: { fontSize: "0.85rem", mb: 0.5, lineHeight: 1, textAlign: "left" },
|
|
37
|
+
sx: { fontSize: "0.85rem", mb: 0.5, lineHeight: 1.2, textAlign: "left" },
|
|
38
38
|
color: "text.lighter",
|
|
39
39
|
children: description
|
|
40
40
|
}
|
package/lib/components/table.js
CHANGED
|
@@ -103,7 +103,7 @@ const Wrapped = (0, _system.styled)(_Datatable.default)`
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
tr.MuiTableRow-root:not(.MuiTableRow-footer):hover {
|
|
106
|
-
background: #
|
|
106
|
+
background: var(--backgrounds-bg-highlight, #eff6ff);
|
|
107
107
|
}
|
|
108
108
|
tr.MuiTableRow-root:last-of-type td:first-of-type {
|
|
109
109
|
border-bottom-left-radius: 8px;
|
|
@@ -97,8 +97,10 @@ const InvoiceTable = _react.default.memo(props => {
|
|
|
97
97
|
refreshDeps: [search]
|
|
98
98
|
});
|
|
99
99
|
const columns = [{
|
|
100
|
-
label: t("
|
|
101
|
-
name: "
|
|
100
|
+
label: t("common.amount"),
|
|
101
|
+
name: "total",
|
|
102
|
+
width: 60,
|
|
103
|
+
align: "right",
|
|
102
104
|
options: {
|
|
103
105
|
customBodyRenderLite: (_, index) => {
|
|
104
106
|
const invoice = data?.list[index];
|
|
@@ -107,13 +109,15 @@ const InvoiceTable = _react.default.memo(props => {
|
|
|
107
109
|
href: link.url,
|
|
108
110
|
target: link.external ? "_blank" : target,
|
|
109
111
|
rel: "noreferrer",
|
|
110
|
-
children:
|
|
112
|
+
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
|
|
113
|
+
children: [(0, _util.formatBNStr)(invoice.total, invoice.paymentCurrency.decimal), "\xA0", invoice.paymentCurrency.symbol]
|
|
114
|
+
})
|
|
111
115
|
});
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
118
|
}, {
|
|
115
|
-
label: t("
|
|
116
|
-
name: "
|
|
119
|
+
label: t("payment.customer.invoice.invoiceNumber"),
|
|
120
|
+
name: "number",
|
|
117
121
|
options: {
|
|
118
122
|
customBodyRenderLite: (_, index) => {
|
|
119
123
|
const invoice = data?.list[index];
|
|
@@ -122,9 +126,7 @@ const InvoiceTable = _react.default.memo(props => {
|
|
|
122
126
|
href: link.url,
|
|
123
127
|
target: link.external ? "_blank" : target,
|
|
124
128
|
rel: "noreferrer",
|
|
125
|
-
children:
|
|
126
|
-
children: [(0, _util.formatBNStr)(invoice.total, invoice.paymentCurrency.decimal), "\xA0", invoice.paymentCurrency.symbol]
|
|
127
|
-
})
|
|
129
|
+
children: invoice?.number
|
|
128
130
|
});
|
|
129
131
|
}
|
|
130
132
|
}
|
package/lib/libs/util.d.ts
CHANGED
|
@@ -105,3 +105,4 @@ export declare function formatQuantityInventory(price: TPrice, quantity: string
|
|
|
105
105
|
export declare function formatSubscriptionStatus(status: string): string;
|
|
106
106
|
export declare function formatAmountPrecisionLimit(amount: string, locale?: string, precision?: number): string;
|
|
107
107
|
export declare function getWordBreakStyle(value: any): 'break-word' | 'break-all';
|
|
108
|
+
export declare function isMobileSafari(): boolean;
|
package/lib/libs/util.js
CHANGED
|
@@ -44,6 +44,7 @@ exports.getSubscriptionStatusColor = getSubscriptionStatusColor;
|
|
|
44
44
|
exports.getTxLink = exports.getSubscriptionTimeSummary = void 0;
|
|
45
45
|
exports.getWebhookStatusColor = getWebhookStatusColor;
|
|
46
46
|
exports.getWordBreakStyle = getWordBreakStyle;
|
|
47
|
+
exports.isMobileSafari = isMobileSafari;
|
|
47
48
|
exports.isPaymentKitMounted = void 0;
|
|
48
49
|
exports.isValidCountry = isValidCountry;
|
|
49
50
|
exports.lazyLoad = lazyLoad;
|
|
@@ -974,4 +975,11 @@ function getWordBreakStyle(value) {
|
|
|
974
975
|
return "break-word";
|
|
975
976
|
}
|
|
976
977
|
return "break-all";
|
|
978
|
+
}
|
|
979
|
+
function isMobileSafari() {
|
|
980
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
981
|
+
const isSafari = ua.indexOf("safari") > -1 && ua.indexOf("chrome") === -1;
|
|
982
|
+
const isMobile = ua.indexOf("mobile") > -1 || /iphone|ipad|ipod/.test(ua);
|
|
983
|
+
const isIOS = /iphone|ipad|ipod/.test(ua);
|
|
984
|
+
return isSafari && isMobile && isIOS;
|
|
977
985
|
}
|
package/lib/locales/en.js
CHANGED
|
@@ -94,7 +94,8 @@ module.exports = (0, _flat.default)({
|
|
|
94
94
|
recoverFrom: "Recovered From",
|
|
95
95
|
quantityLimitPerCheckout: "Exceed purchase limit",
|
|
96
96
|
quantityNotEnough: "Exceed inventory",
|
|
97
|
-
amountPrecisionLimit: "Amount decimal places must be less than or equal to {precision}"
|
|
97
|
+
amountPrecisionLimit: "Amount decimal places must be less than or equal to {precision}",
|
|
98
|
+
saveAsDefaultPriceSuccess: "Set default price successfully"
|
|
98
99
|
},
|
|
99
100
|
payment: {
|
|
100
101
|
checkout: {
|
|
@@ -233,7 +234,7 @@ module.exports = (0, _flat.default)({
|
|
|
233
234
|
recover: {
|
|
234
235
|
button: "Renew",
|
|
235
236
|
title: "Renew your subscription",
|
|
236
|
-
description: "Your subscription will
|
|
237
|
+
description: "Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue"
|
|
237
238
|
},
|
|
238
239
|
changePlan: {
|
|
239
240
|
button: "Update",
|
package/lib/locales/zh.js
CHANGED
|
@@ -94,7 +94,8 @@ module.exports = (0, _flat.default)({
|
|
|
94
94
|
recoverFrom: "\u6062\u590D\u81EA",
|
|
95
95
|
quantityLimitPerCheckout: "\u8D85\u51FA\u8D2D\u4E70\u9650\u5236",
|
|
96
96
|
quantityNotEnough: "\u5E93\u5B58\u4E0D\u8DB3",
|
|
97
|
-
amountPrecisionLimit: "\u91D1\u989D\u5C0F\u6570\u4F4D\u6570\u5FC5\u987B\u5728 {precision} \u4F4D\u4EE5\u5185"
|
|
97
|
+
amountPrecisionLimit: "\u91D1\u989D\u5C0F\u6570\u4F4D\u6570\u5FC5\u987B\u5728 {precision} \u4F4D\u4EE5\u5185",
|
|
98
|
+
saveAsDefaultPriceSuccess: "\u8BBE\u7F6E\u9ED8\u8BA4\u4EF7\u683C\u6210\u529F"
|
|
98
99
|
},
|
|
99
100
|
payment: {
|
|
100
101
|
checkout: {
|
|
@@ -233,7 +234,7 @@ module.exports = (0, _flat.default)({
|
|
|
233
234
|
recover: {
|
|
234
235
|
button: "\u7EED\u8BA2",
|
|
235
236
|
title: "\u7EED\u8BA2\u60A8\u7684\u8BA2\u9605",
|
|
236
|
-
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u4E0D\
|
|
237
|
+
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u4E0D\u4F1A\u88AB\u53D6\u6D88\uFF0C\u5E76\u5C06\u5728{date}\u81EA\u52A8\u7EED\u8BA2\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u7EE7\u7EED"
|
|
237
238
|
},
|
|
238
239
|
changePlan: {
|
|
239
240
|
button: "\u66F4\u65B0",
|
|
@@ -18,6 +18,7 @@ var _reactHookForm = require("react-hook-form");
|
|
|
18
18
|
var _ufo = require("ufo");
|
|
19
19
|
var _useBus = require("use-bus");
|
|
20
20
|
var _isEmail = _interopRequireDefault(require("validator/es/lib/isEmail"));
|
|
21
|
+
var _lodash = require("lodash");
|
|
21
22
|
var _confirm = _interopRequireDefault(require("../../components/confirm"));
|
|
22
23
|
var _input = _interopRequireDefault(require("../../components/input"));
|
|
23
24
|
var _payment = require("../../contexts/payment");
|
|
@@ -28,6 +29,7 @@ var _address = _interopRequireDefault(require("./address"));
|
|
|
28
29
|
var _currency = _interopRequireDefault(require("./currency"));
|
|
29
30
|
var _phone = _interopRequireDefault(require("./phone"));
|
|
30
31
|
var _stripe = _interopRequireDefault(require("./stripe"));
|
|
32
|
+
var _mobile = require("../../hooks/mobile");
|
|
31
33
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
32
34
|
const phoneUtil = _googleLibphonenumber.PhoneNumberUtil.getInstance();
|
|
33
35
|
const waitForCheckoutComplete = async sessionId => {
|
|
@@ -66,6 +68,9 @@ function PaymentForm({
|
|
|
66
68
|
const {
|
|
67
69
|
t
|
|
68
70
|
} = (0, _context.useLocaleContext)();
|
|
71
|
+
const {
|
|
72
|
+
isMobile
|
|
73
|
+
} = (0, _mobile.useMobile)();
|
|
69
74
|
const {
|
|
70
75
|
session,
|
|
71
76
|
connect,
|
|
@@ -76,8 +81,12 @@ function PaymentForm({
|
|
|
76
81
|
control,
|
|
77
82
|
getValues,
|
|
78
83
|
setValue,
|
|
79
|
-
handleSubmit
|
|
84
|
+
handleSubmit,
|
|
85
|
+
formState: {
|
|
86
|
+
errors
|
|
87
|
+
}
|
|
80
88
|
} = (0, _reactHookForm.useFormContext)();
|
|
89
|
+
const errorRef = (0, _react.useRef)(null);
|
|
81
90
|
const quantityInventoryStatus = (0, _react.useMemo)(() => {
|
|
82
91
|
let status = true;
|
|
83
92
|
for (const item of checkoutSession.line_items) {
|
|
@@ -190,6 +199,13 @@ function PaymentForm({
|
|
|
190
199
|
});
|
|
191
200
|
}
|
|
192
201
|
};
|
|
202
|
+
(0, _react.useEffect)(() => {
|
|
203
|
+
if (errorRef.current && !(0, _lodash.isEmpty)(errors) && isMobile) {
|
|
204
|
+
errorRef.current.scrollIntoView({
|
|
205
|
+
behavior: "smooth"
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}, [errors, isMobile]);
|
|
193
209
|
const onUserLoggedIn = async () => {
|
|
194
210
|
const {
|
|
195
211
|
data: profile
|
|
@@ -318,6 +334,11 @@ function PaymentForm({
|
|
|
318
334
|
});
|
|
319
335
|
};
|
|
320
336
|
const onAction = () => {
|
|
337
|
+
if (errorRef.current && !(0, _lodash.isEmpty)(errors) && isMobile) {
|
|
338
|
+
errorRef.current.scrollIntoView({
|
|
339
|
+
behavior: "smooth"
|
|
340
|
+
});
|
|
341
|
+
}
|
|
321
342
|
if (session?.user) {
|
|
322
343
|
if (hasDidWallet(session.user)) {
|
|
323
344
|
handleSubmit(onFormSubmit, onFormError)();
|
|
@@ -412,7 +433,9 @@ function PaymentForm({
|
|
|
412
433
|
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
|
|
413
434
|
direction: "column",
|
|
414
435
|
className: "cko-payment-form",
|
|
436
|
+
id: "cko-payment-form",
|
|
415
437
|
spacing: 0,
|
|
438
|
+
ref: !(0, _lodash.isEmpty)(errors) ? errorRef : void 0,
|
|
416
439
|
sx: {
|
|
417
440
|
flex: 1,
|
|
418
441
|
overflow: "auto"
|
|
@@ -477,16 +500,19 @@ function PaymentForm({
|
|
|
477
500
|
in: true,
|
|
478
501
|
children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
|
|
479
502
|
className: "cko-payment-submit",
|
|
480
|
-
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
503
|
+
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
|
|
504
|
+
className: "cko-payment-submit-btn",
|
|
505
|
+
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_lab.LoadingButton, {
|
|
506
|
+
variant: "contained",
|
|
507
|
+
color: "primary",
|
|
508
|
+
size: "large",
|
|
509
|
+
className: "cko-submit-button",
|
|
510
|
+
onClick: onAction,
|
|
511
|
+
fullWidth: true,
|
|
512
|
+
disabled: state.submitting || state.paying || state.stripePaying || !quantityInventoryStatus || !payable,
|
|
513
|
+
loading: state.submitting || state.paying,
|
|
514
|
+
children: state.submitting || state.paying ? t("payment.checkout.processing") : buttonText
|
|
515
|
+
})
|
|
490
516
|
}), ["subscription", "setup"].includes(checkoutSession.mode) && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
491
517
|
sx: {
|
|
492
518
|
mt: 2.5,
|
package/lib/payment/index.js
CHANGED
|
@@ -81,6 +81,24 @@ function PaymentInner({
|
|
|
81
81
|
})
|
|
82
82
|
}
|
|
83
83
|
});
|
|
84
|
+
(0, _react.useEffect)(() => {
|
|
85
|
+
if (!(0, _util2.isMobileSafari)()) {
|
|
86
|
+
return () => {};
|
|
87
|
+
}
|
|
88
|
+
let scrollTop = 0;
|
|
89
|
+
const focusinHandler = () => {
|
|
90
|
+
scrollTop = window.scrollY;
|
|
91
|
+
};
|
|
92
|
+
const focusoutHandler = () => {
|
|
93
|
+
window.scrollTo(0, scrollTop);
|
|
94
|
+
};
|
|
95
|
+
document.body.addEventListener("focusin", focusinHandler);
|
|
96
|
+
document.body.addEventListener("focusout", focusoutHandler);
|
|
97
|
+
return () => {
|
|
98
|
+
document.body.removeEventListener("focusin", focusinHandler);
|
|
99
|
+
document.body.removeEventListener("focusout", focusoutHandler);
|
|
100
|
+
};
|
|
101
|
+
}, []);
|
|
84
102
|
const currencyId = (0, _reactHookForm.useWatch)({
|
|
85
103
|
control: methods.control,
|
|
86
104
|
name: "payment_currency",
|
|
@@ -280,7 +298,11 @@ function Payment({
|
|
|
280
298
|
setLivemode
|
|
281
299
|
} = (0, _payment.usePaymentContext)();
|
|
282
300
|
const [delay, setDelay] = (0, _react.useState)(false);
|
|
301
|
+
const {
|
|
302
|
+
isMobile
|
|
303
|
+
} = (0, _mobile.useMobile)();
|
|
283
304
|
const hideSummaryCard = mode.endsWith("-minimal") || !showCheckoutSummary;
|
|
305
|
+
const isMobileSafariEnv = (0, _util2.isMobileSafari)();
|
|
284
306
|
(0, _react.useEffect)(() => {
|
|
285
307
|
setTimeout(() => {
|
|
286
308
|
setDelay(true);
|
|
@@ -359,7 +381,7 @@ function Payment({
|
|
|
359
381
|
flexDirection: "column",
|
|
360
382
|
sx: {
|
|
361
383
|
height: mode === "standalone" ? "100vh" : "auto",
|
|
362
|
-
overflow: "hidden"
|
|
384
|
+
overflow: isMobileSafariEnv ? "visible" : "hidden"
|
|
363
385
|
},
|
|
364
386
|
children: [mode === "standalone" ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_Header.default, {
|
|
365
387
|
meta: void 0,
|
|
@@ -375,7 +397,39 @@ function Payment({
|
|
|
375
397
|
}) : null, /* @__PURE__ */(0, _jsxRuntime.jsxs)(Root, {
|
|
376
398
|
mode,
|
|
377
399
|
sx: {
|
|
378
|
-
flex: 1
|
|
400
|
+
flex: 1,
|
|
401
|
+
overflow: {
|
|
402
|
+
xs: isMobileSafariEnv ? "visible" : "auto",
|
|
403
|
+
md: "hidden"
|
|
404
|
+
},
|
|
405
|
+
...(isMobile && mode === "standalone" ? {
|
|
406
|
+
".cko-payment-submit-btn": {
|
|
407
|
+
position: "fixed",
|
|
408
|
+
bottom: 20,
|
|
409
|
+
left: 0,
|
|
410
|
+
right: 0,
|
|
411
|
+
zIndex: 999,
|
|
412
|
+
background: "#fff",
|
|
413
|
+
padding: "10px",
|
|
414
|
+
textAlign: "center",
|
|
415
|
+
button: {
|
|
416
|
+
color: "#fff",
|
|
417
|
+
maxWidth: 542
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
".cko-footer": {
|
|
421
|
+
position: "fixed",
|
|
422
|
+
bottom: 0,
|
|
423
|
+
left: 0,
|
|
424
|
+
right: 0,
|
|
425
|
+
zIndex: 999,
|
|
426
|
+
background: "#fff",
|
|
427
|
+
marginBottom: 0
|
|
428
|
+
},
|
|
429
|
+
".cko-payment": {
|
|
430
|
+
paddingBottom: "100px"
|
|
431
|
+
}
|
|
432
|
+
} : {})
|
|
379
433
|
},
|
|
380
434
|
children: [goBack && /* @__PURE__ */(0, _jsxRuntime.jsx)(_iconsMaterial.ArrowBackOutlined, {
|
|
381
435
|
sx: {
|
|
@@ -541,7 +595,6 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
|
|
|
541
595
|
@media (max-width: ${({
|
|
542
596
|
theme
|
|
543
597
|
}) => theme.breakpoints.values.md}px) {
|
|
544
|
-
background: ${props => props.mode === "standalone" ? "var(--backgrounds-bg-subtle, #F9FAFB)" : "transparent"};
|
|
545
598
|
padding-top: 0;
|
|
546
599
|
overflow: auto;
|
|
547
600
|
&:before {
|
|
@@ -550,7 +603,8 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
|
|
|
550
603
|
.cko-container {
|
|
551
604
|
flex-direction: column;
|
|
552
605
|
align-items: center;
|
|
553
|
-
|
|
606
|
+
justify-content: flex-start;
|
|
607
|
+
gap: 0;
|
|
554
608
|
overflow: visible;
|
|
555
609
|
min-width: 200px;
|
|
556
610
|
}
|
|
@@ -563,6 +617,7 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
|
|
|
563
617
|
width: 100%;
|
|
564
618
|
height: fit-content;
|
|
565
619
|
flex: none;
|
|
620
|
+
border-top: 1px solid var(--stroke-border-base, #eff1f5);
|
|
566
621
|
&:before {
|
|
567
622
|
display: none;
|
|
568
623
|
}
|
|
@@ -56,7 +56,7 @@ function ProductCard({
|
|
|
56
56
|
sx: {
|
|
57
57
|
fontWeight: 500,
|
|
58
58
|
mb: 0.5,
|
|
59
|
-
lineHeight: 1,
|
|
59
|
+
lineHeight: 1.2,
|
|
60
60
|
fontSize: 16
|
|
61
61
|
},
|
|
62
62
|
color: "text.primary",
|
|
@@ -67,7 +67,7 @@ function ProductCard({
|
|
|
67
67
|
sx: {
|
|
68
68
|
fontSize: "0.85rem",
|
|
69
69
|
mb: 0.5,
|
|
70
|
-
lineHeight: 1,
|
|
70
|
+
lineHeight: 1.2,
|
|
71
71
|
textAlign: "left"
|
|
72
72
|
},
|
|
73
73
|
color: "text.lighter",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/payment-react",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.30",
|
|
4
4
|
"description": "Reusable react components for payment kit v2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"@babel/core": "^7.25.2",
|
|
94
94
|
"@babel/preset-env": "^7.25.2",
|
|
95
95
|
"@babel/preset-react": "^7.24.7",
|
|
96
|
-
"@blocklet/payment-types": "1.14.
|
|
96
|
+
"@blocklet/payment-types": "1.14.30",
|
|
97
97
|
"@storybook/addon-essentials": "^7.6.20",
|
|
98
98
|
"@storybook/addon-interactions": "^7.6.20",
|
|
99
99
|
"@storybook/addon-links": "^7.6.20",
|
|
@@ -123,5 +123,5 @@
|
|
|
123
123
|
"vite-plugin-babel": "^1.2.0",
|
|
124
124
|
"vite-plugin-node-polyfills": "^0.21.0"
|
|
125
125
|
},
|
|
126
|
-
"gitHead": "
|
|
126
|
+
"gitHead": "f98dee7bce684f81f4e060b942efaa9ad8730b55"
|
|
127
127
|
}
|
package/src/components/table.tsx
CHANGED
|
@@ -93,7 +93,7 @@ const Wrapped = styled(Datatable)`
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
tr.MuiTableRow-root:not(.MuiTableRow-footer):hover {
|
|
96
|
-
background: #
|
|
96
|
+
background: var(--backgrounds-bg-highlight, #eff6ff);
|
|
97
97
|
}
|
|
98
98
|
tr.MuiTableRow-root:last-of-type td:first-of-type {
|
|
99
99
|
border-bottom-left-radius: 8px;
|
|
@@ -124,39 +124,40 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
|
|
|
124
124
|
|
|
125
125
|
const columns = [
|
|
126
126
|
{
|
|
127
|
-
label: t('
|
|
128
|
-
name: '
|
|
127
|
+
label: t('common.amount'),
|
|
128
|
+
name: 'total',
|
|
129
|
+
width: 60,
|
|
130
|
+
align: 'right',
|
|
129
131
|
options: {
|
|
130
132
|
customBodyRenderLite: (_: string, index: number) => {
|
|
131
133
|
const invoice = data?.list[index] as TInvoiceExpanded;
|
|
132
134
|
const link = getInvoiceLink(invoice, action);
|
|
133
135
|
return (
|
|
134
136
|
<a href={link.url} target={link.external ? '_blank' : target} rel="noreferrer">
|
|
135
|
-
|
|
137
|
+
<Typography>
|
|
138
|
+
{formatBNStr(invoice.total, invoice.paymentCurrency.decimal)}
|
|
139
|
+
{invoice.paymentCurrency.symbol}
|
|
140
|
+
</Typography>
|
|
136
141
|
</a>
|
|
137
142
|
);
|
|
138
143
|
},
|
|
139
144
|
},
|
|
140
145
|
},
|
|
141
146
|
{
|
|
142
|
-
label: t('
|
|
143
|
-
name: '
|
|
147
|
+
label: t('payment.customer.invoice.invoiceNumber'),
|
|
148
|
+
name: 'number',
|
|
144
149
|
options: {
|
|
145
150
|
customBodyRenderLite: (_: string, index: number) => {
|
|
146
151
|
const invoice = data?.list[index] as TInvoiceExpanded;
|
|
147
152
|
const link = getInvoiceLink(invoice, action);
|
|
148
153
|
return (
|
|
149
154
|
<a href={link.url} target={link.external ? '_blank' : target} rel="noreferrer">
|
|
150
|
-
|
|
151
|
-
{formatBNStr(invoice.total, invoice.paymentCurrency.decimal)}
|
|
152
|
-
{invoice.paymentCurrency.symbol}
|
|
153
|
-
</Typography>
|
|
155
|
+
{invoice?.number}
|
|
154
156
|
</a>
|
|
155
157
|
);
|
|
156
158
|
},
|
|
157
159
|
},
|
|
158
160
|
},
|
|
159
|
-
|
|
160
161
|
{
|
|
161
162
|
label: t('common.updatedAt'),
|
|
162
163
|
name: 'name',
|
package/src/libs/util.ts
CHANGED
|
@@ -1042,3 +1042,11 @@ export function getWordBreakStyle(value: any): 'break-word' | 'break-all' {
|
|
|
1042
1042
|
}
|
|
1043
1043
|
return 'break-all';
|
|
1044
1044
|
}
|
|
1045
|
+
|
|
1046
|
+
export function isMobileSafari() {
|
|
1047
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
1048
|
+
const isSafari = ua.indexOf('safari') > -1 && ua.indexOf('chrome') === -1;
|
|
1049
|
+
const isMobile = ua.indexOf('mobile') > -1 || /iphone|ipad|ipod/.test(ua);
|
|
1050
|
+
const isIOS = /iphone|ipad|ipod/.test(ua);
|
|
1051
|
+
return isSafari && isMobile && isIOS;
|
|
1052
|
+
}
|
package/src/locales/en.tsx
CHANGED
|
@@ -90,6 +90,7 @@ export default flat({
|
|
|
90
90
|
quantityLimitPerCheckout: 'Exceed purchase limit',
|
|
91
91
|
quantityNotEnough: 'Exceed inventory',
|
|
92
92
|
amountPrecisionLimit: 'Amount decimal places must be less than or equal to {precision}',
|
|
93
|
+
saveAsDefaultPriceSuccess: 'Set default price successfully',
|
|
93
94
|
},
|
|
94
95
|
payment: {
|
|
95
96
|
checkout: {
|
|
@@ -234,7 +235,8 @@ export default flat({
|
|
|
234
235
|
recover: {
|
|
235
236
|
button: 'Renew',
|
|
236
237
|
title: 'Renew your subscription',
|
|
237
|
-
description:
|
|
238
|
+
description:
|
|
239
|
+
'Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue',
|
|
238
240
|
},
|
|
239
241
|
changePlan: {
|
|
240
242
|
button: 'Update',
|
package/src/locales/zh.tsx
CHANGED
|
@@ -90,6 +90,7 @@ export default flat({
|
|
|
90
90
|
quantityLimitPerCheckout: '超出购买限制',
|
|
91
91
|
quantityNotEnough: '库存不足',
|
|
92
92
|
amountPrecisionLimit: '金额小数位数必须在 {precision} 位以内',
|
|
93
|
+
saveAsDefaultPriceSuccess: '设置默认价格成功',
|
|
93
94
|
},
|
|
94
95
|
payment: {
|
|
95
96
|
checkout: {
|
|
@@ -228,7 +229,7 @@ export default flat({
|
|
|
228
229
|
recover: {
|
|
229
230
|
button: '续订',
|
|
230
231
|
title: '续订您的订阅',
|
|
231
|
-
description: '
|
|
232
|
+
description: '您的订阅将不会被取消,并将在{date}自动续订,请确认是否继续',
|
|
232
233
|
},
|
|
233
234
|
changePlan: {
|
|
234
235
|
button: '更新',
|
|
@@ -11,16 +11,17 @@ import type {
|
|
|
11
11
|
TPaymentMethodExpanded,
|
|
12
12
|
} from '@blocklet/payment-types';
|
|
13
13
|
import { LoadingButton } from '@mui/lab';
|
|
14
|
-
import { Divider, Fade, FormLabel, Stack, Typography } from '@mui/material';
|
|
14
|
+
import { Box, Divider, Fade, FormLabel, Stack, Typography } from '@mui/material';
|
|
15
15
|
import { useMemoizedFn, useSetState } from 'ahooks';
|
|
16
16
|
import { PhoneNumberUtil } from 'google-libphonenumber';
|
|
17
17
|
import pWaitFor from 'p-wait-for';
|
|
18
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
18
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
19
19
|
import { Controller, useFormContext, useWatch } from 'react-hook-form';
|
|
20
20
|
import { joinURL } from 'ufo';
|
|
21
21
|
import { dispatch } from 'use-bus';
|
|
22
22
|
import isEmail from 'validator/es/lib/isEmail';
|
|
23
23
|
|
|
24
|
+
import { isEmpty } from 'lodash';
|
|
24
25
|
import ConfirmDialog from '../../components/confirm';
|
|
25
26
|
import FormInput from '../../components/input';
|
|
26
27
|
import { usePaymentContext } from '../../contexts/payment';
|
|
@@ -39,6 +40,7 @@ import AddressForm from './address';
|
|
|
39
40
|
import CurrencySelector from './currency';
|
|
40
41
|
import PhoneInput from './phone';
|
|
41
42
|
import StripeCheckout from './stripe';
|
|
43
|
+
import { useMobile } from '../../hooks/mobile';
|
|
42
44
|
|
|
43
45
|
const phoneUtil = PhoneNumberUtil.getInstance();
|
|
44
46
|
|
|
@@ -97,9 +99,17 @@ export default function PaymentForm({
|
|
|
97
99
|
}: PageData) {
|
|
98
100
|
// const theme = useTheme();
|
|
99
101
|
const { t } = useLocaleContext();
|
|
102
|
+
const { isMobile } = useMobile();
|
|
100
103
|
const { session, connect, payable } = usePaymentContext();
|
|
101
104
|
const subscription = useSubscription('events');
|
|
102
|
-
const {
|
|
105
|
+
const {
|
|
106
|
+
control,
|
|
107
|
+
getValues,
|
|
108
|
+
setValue,
|
|
109
|
+
handleSubmit,
|
|
110
|
+
formState: { errors },
|
|
111
|
+
} = useFormContext();
|
|
112
|
+
const errorRef = useRef<HTMLDivElement | null>(null);
|
|
103
113
|
const quantityInventoryStatus = useMemo(() => {
|
|
104
114
|
let status = true;
|
|
105
115
|
for (const item of checkoutSession.line_items) {
|
|
@@ -236,6 +246,12 @@ export default function PaymentForm({
|
|
|
236
246
|
}
|
|
237
247
|
};
|
|
238
248
|
|
|
249
|
+
useEffect(() => {
|
|
250
|
+
if (errorRef.current && !isEmpty(errors) && isMobile) {
|
|
251
|
+
errorRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
252
|
+
}
|
|
253
|
+
}, [errors, isMobile]);
|
|
254
|
+
|
|
239
255
|
const onUserLoggedIn = async () => {
|
|
240
256
|
const { data: profile } = await api.get('/api/customers/me?fallback=1');
|
|
241
257
|
if (profile) {
|
|
@@ -345,6 +361,9 @@ export default function PaymentForm({
|
|
|
345
361
|
};
|
|
346
362
|
|
|
347
363
|
const onAction = () => {
|
|
364
|
+
if (errorRef.current && !isEmpty(errors) && isMobile) {
|
|
365
|
+
errorRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
366
|
+
}
|
|
348
367
|
if (session?.user) {
|
|
349
368
|
if (hasDidWallet(session.user)) {
|
|
350
369
|
handleSubmit(onFormSubmit, onFormError)();
|
|
@@ -428,7 +447,13 @@ export default function PaymentForm({
|
|
|
428
447
|
{/* <Typography sx={{ color: 'text.secondary', fontWeight: 600 }}>{t('payment.checkout.contact')}</Typography> */}
|
|
429
448
|
{/* {isColumnLayout || mode !== 'standalone' ? null : <UserButtons />} */}
|
|
430
449
|
</Stack>
|
|
431
|
-
<Stack
|
|
450
|
+
<Stack
|
|
451
|
+
direction="column"
|
|
452
|
+
className="cko-payment-form"
|
|
453
|
+
id="cko-payment-form"
|
|
454
|
+
spacing={0}
|
|
455
|
+
ref={!isEmpty(errors) ? (errorRef as any) : undefined}
|
|
456
|
+
sx={{ flex: 1, overflow: 'auto' }}>
|
|
432
457
|
<FormLabel className="base-label">{t('payment.checkout.customer.name')}</FormLabel>
|
|
433
458
|
<FormInput
|
|
434
459
|
name="customer_name"
|
|
@@ -481,17 +506,20 @@ export default function PaymentForm({
|
|
|
481
506
|
<Divider sx={{ mt: 2.5, mb: 2.5 }} />
|
|
482
507
|
<Fade in>
|
|
483
508
|
<Stack className="cko-payment-submit">
|
|
484
|
-
<
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
509
|
+
<Box className="cko-payment-submit-btn">
|
|
510
|
+
<LoadingButton
|
|
511
|
+
variant="contained"
|
|
512
|
+
color="primary"
|
|
513
|
+
size="large"
|
|
514
|
+
className="cko-submit-button"
|
|
515
|
+
onClick={onAction}
|
|
516
|
+
fullWidth
|
|
517
|
+
disabled={state.submitting || state.paying || state.stripePaying || !quantityInventoryStatus || !payable}
|
|
518
|
+
loading={state.submitting || state.paying}>
|
|
519
|
+
{state.submitting || state.paying ? t('payment.checkout.processing') : buttonText}
|
|
520
|
+
</LoadingButton>
|
|
521
|
+
</Box>
|
|
522
|
+
|
|
495
523
|
{['subscription', 'setup'].includes(checkoutSession.mode) && (
|
|
496
524
|
<Typography sx={{ mt: 2.5, color: 'text.lighter', fontSize: '0.9rem', lineHeight: '1.1rem' }}>
|
|
497
525
|
{t('payment.checkout.confirm', { payee })}
|
package/src/payment/index.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/indent */
|
|
1
2
|
/* eslint-disable no-nested-ternary */
|
|
2
3
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
4
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
@@ -21,7 +22,7 @@ import type { LiteralUnion } from 'type-fest';
|
|
|
21
22
|
|
|
22
23
|
import { usePaymentContext } from '../contexts/payment';
|
|
23
24
|
import api from '../libs/api';
|
|
24
|
-
import { findCurrency, formatError, getStatementDescriptor, isValidCountry } from '../libs/util';
|
|
25
|
+
import { findCurrency, formatError, getStatementDescriptor, isMobileSafari, isValidCountry } from '../libs/util';
|
|
25
26
|
import { CheckoutCallbacks, CheckoutContext, CheckoutFormData } from '../types';
|
|
26
27
|
import PaymentError from './error';
|
|
27
28
|
import CheckoutFooter from './footer';
|
|
@@ -86,6 +87,25 @@ function PaymentInner({
|
|
|
86
87
|
},
|
|
87
88
|
});
|
|
88
89
|
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!isMobileSafari()) {
|
|
92
|
+
return () => {};
|
|
93
|
+
}
|
|
94
|
+
let scrollTop = 0;
|
|
95
|
+
const focusinHandler = () => {
|
|
96
|
+
scrollTop = window.scrollY;
|
|
97
|
+
};
|
|
98
|
+
const focusoutHandler = () => {
|
|
99
|
+
window.scrollTo(0, scrollTop);
|
|
100
|
+
};
|
|
101
|
+
document.body.addEventListener('focusin', focusinHandler);
|
|
102
|
+
document.body.addEventListener('focusout', focusoutHandler);
|
|
103
|
+
return () => {
|
|
104
|
+
document.body.removeEventListener('focusin', focusinHandler);
|
|
105
|
+
document.body.removeEventListener('focusout', focusoutHandler);
|
|
106
|
+
};
|
|
107
|
+
}, []);
|
|
108
|
+
|
|
89
109
|
const currencyId = useWatch({ control: methods.control, name: 'payment_currency', defaultValue: defaultCurrencyId });
|
|
90
110
|
const currency =
|
|
91
111
|
(findCurrency(paymentMethods as TPaymentMethodExpanded[], currencyId as string) as TPaymentCurrency) ||
|
|
@@ -260,7 +280,9 @@ export default function Payment({
|
|
|
260
280
|
const { t } = useLocaleContext();
|
|
261
281
|
const { refresh, livemode, setLivemode } = usePaymentContext();
|
|
262
282
|
const [delay, setDelay] = useState(false);
|
|
283
|
+
const { isMobile } = useMobile();
|
|
263
284
|
const hideSummaryCard = mode.endsWith('-minimal') || !showCheckoutSummary;
|
|
285
|
+
const isMobileSafariEnv = isMobileSafari();
|
|
264
286
|
|
|
265
287
|
useEffect(() => {
|
|
266
288
|
setTimeout(() => {
|
|
@@ -342,11 +364,15 @@ export default function Payment({
|
|
|
342
364
|
/>
|
|
343
365
|
);
|
|
344
366
|
};
|
|
367
|
+
|
|
345
368
|
return (
|
|
346
369
|
<Stack
|
|
347
370
|
display="flex"
|
|
348
371
|
flexDirection="column"
|
|
349
|
-
sx={{
|
|
372
|
+
sx={{
|
|
373
|
+
height: mode === 'standalone' ? '100vh' : 'auto',
|
|
374
|
+
overflow: isMobileSafariEnv ? 'visible' : 'hidden',
|
|
375
|
+
}}>
|
|
350
376
|
{mode === 'standalone' ? (
|
|
351
377
|
<Header
|
|
352
378
|
meta={undefined}
|
|
@@ -359,7 +385,45 @@ export default function Payment({
|
|
|
359
385
|
sx={{ borderBottom: '1px solid var(--stroke-border-base, #EFF1F5)' }}
|
|
360
386
|
/>
|
|
361
387
|
) : null}
|
|
362
|
-
<Root
|
|
388
|
+
<Root
|
|
389
|
+
mode={mode}
|
|
390
|
+
sx={{
|
|
391
|
+
flex: 1,
|
|
392
|
+
overflow: {
|
|
393
|
+
xs: isMobileSafariEnv ? 'visible' : 'auto',
|
|
394
|
+
md: 'hidden',
|
|
395
|
+
},
|
|
396
|
+
...(isMobile && mode === 'standalone'
|
|
397
|
+
? {
|
|
398
|
+
'.cko-payment-submit-btn': {
|
|
399
|
+
position: 'fixed',
|
|
400
|
+
bottom: 20,
|
|
401
|
+
left: 0,
|
|
402
|
+
right: 0,
|
|
403
|
+
zIndex: 999,
|
|
404
|
+
background: '#fff',
|
|
405
|
+
padding: '10px',
|
|
406
|
+
textAlign: 'center',
|
|
407
|
+
button: {
|
|
408
|
+
color: '#fff',
|
|
409
|
+
maxWidth: 542,
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
'.cko-footer': {
|
|
413
|
+
position: 'fixed',
|
|
414
|
+
bottom: 0,
|
|
415
|
+
left: 0,
|
|
416
|
+
right: 0,
|
|
417
|
+
zIndex: 999,
|
|
418
|
+
background: '#fff',
|
|
419
|
+
marginBottom: 0,
|
|
420
|
+
},
|
|
421
|
+
'.cko-payment': {
|
|
422
|
+
paddingBottom: '100px',
|
|
423
|
+
},
|
|
424
|
+
}
|
|
425
|
+
: {}),
|
|
426
|
+
}}>
|
|
363
427
|
{goBack && (
|
|
364
428
|
<ArrowBackOutlined
|
|
365
429
|
sx={{ mr: 0.5, color: 'text.secondary', alignSelf: 'flex-start', margin: '16px 0', cursor: 'pointer' }}
|
|
@@ -517,7 +581,6 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
|
|
|
517
581
|
}
|
|
518
582
|
|
|
519
583
|
@media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
|
|
520
|
-
background: ${(props) => (props.mode === 'standalone' ? 'var(--backgrounds-bg-subtle, #F9FAFB)' : 'transparent')};
|
|
521
584
|
padding-top: 0;
|
|
522
585
|
overflow: auto;
|
|
523
586
|
&:before {
|
|
@@ -526,7 +589,8 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
|
|
|
526
589
|
.cko-container {
|
|
527
590
|
flex-direction: column;
|
|
528
591
|
align-items: center;
|
|
529
|
-
|
|
592
|
+
justify-content: flex-start;
|
|
593
|
+
gap: 0;
|
|
530
594
|
overflow: visible;
|
|
531
595
|
min-width: 200px;
|
|
532
596
|
}
|
|
@@ -539,6 +603,7 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
|
|
|
539
603
|
width: 100%;
|
|
540
604
|
height: fit-content;
|
|
541
605
|
flex: none;
|
|
606
|
+
border-top: 1px solid var(--stroke-border-base, #eff1f5);
|
|
542
607
|
&:before {
|
|
543
608
|
display: none;
|
|
544
609
|
}
|
|
@@ -33,7 +33,7 @@ export default function ProductCard({ size, variant, name, logo, description, ex
|
|
|
33
33
|
className="cko-ellipsis"
|
|
34
34
|
variant="body1"
|
|
35
35
|
title={name}
|
|
36
|
-
sx={{ fontWeight: 500, mb: 0.5, lineHeight: 1, fontSize: 16 }}
|
|
36
|
+
sx={{ fontWeight: 500, mb: 0.5, lineHeight: 1.2, fontSize: 16 }}
|
|
37
37
|
color="text.primary">
|
|
38
38
|
{name}
|
|
39
39
|
</Typography>
|
|
@@ -41,7 +41,7 @@ export default function ProductCard({ size, variant, name, logo, description, ex
|
|
|
41
41
|
<Typography
|
|
42
42
|
variant="body1"
|
|
43
43
|
title={description}
|
|
44
|
-
sx={{ fontSize: '0.85rem', mb: 0.5, lineHeight: 1, textAlign: 'left' }}
|
|
44
|
+
sx={{ fontSize: '0.85rem', mb: 0.5, lineHeight: 1.2, textAlign: 'left' }}
|
|
45
45
|
color="text.lighter">
|
|
46
46
|
{description}
|
|
47
47
|
</Typography>
|