@blocklet/payment-react 1.18.34 → 1.18.36

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.
Files changed (42) hide show
  1. package/es/components/country-select.d.ts +1 -0
  2. package/es/components/country-select.js +359 -276
  3. package/es/contexts/payment.js +21 -1
  4. package/es/libs/cached-request.d.ts +1 -1
  5. package/es/libs/util.d.ts +1 -0
  6. package/es/libs/util.js +13 -0
  7. package/es/libs/validator.d.ts +1 -0
  8. package/es/libs/validator.js +14 -0
  9. package/es/locales/en.js +2 -1
  10. package/es/locales/zh.js +2 -1
  11. package/es/payment/form/address.d.ts +5 -1
  12. package/es/payment/form/address.js +27 -14
  13. package/es/payment/form/index.js +43 -10
  14. package/es/payment/form/phone.js +2 -1
  15. package/es/payment/form/stripe/form.js +1 -0
  16. package/lib/components/country-select.d.ts +1 -0
  17. package/lib/components/country-select.js +188 -80
  18. package/lib/contexts/payment.js +21 -0
  19. package/lib/libs/cached-request.d.ts +1 -1
  20. package/lib/libs/util.d.ts +1 -0
  21. package/lib/libs/util.js +16 -1
  22. package/lib/libs/validator.d.ts +1 -0
  23. package/lib/libs/validator.js +14 -0
  24. package/lib/locales/en.js +2 -1
  25. package/lib/locales/zh.js +2 -1
  26. package/lib/payment/form/address.d.ts +5 -1
  27. package/lib/payment/form/address.js +23 -13
  28. package/lib/payment/form/index.js +39 -10
  29. package/lib/payment/form/phone.js +2 -1
  30. package/lib/payment/form/stripe/form.js +1 -0
  31. package/package.json +6 -6
  32. package/src/components/country-select.tsx +381 -290
  33. package/src/contexts/payment.tsx +29 -1
  34. package/src/libs/cached-request.ts +1 -1
  35. package/src/libs/util.ts +15 -0
  36. package/src/libs/validator.ts +14 -0
  37. package/src/locales/en.tsx +1 -0
  38. package/src/locales/zh.tsx +1 -0
  39. package/src/payment/form/address.tsx +26 -11
  40. package/src/payment/form/index.tsx +39 -7
  41. package/src/payment/form/phone.tsx +1 -0
  42. package/src/payment/form/stripe/form.tsx +1 -0
@@ -1,7 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Alert } from "@mui/material";
3
3
  import { useLocalStorageState, useRequest } from "ahooks";
4
- import { createContext, useContext, useState } from "react";
4
+ import { createContext, useContext, useEffect, useState } from "react";
5
5
  import api from "../libs/api.js";
6
6
  import { getPrefix } from "../libs/util.js";
7
7
  import { CachedRequest } from "../libs/cached-request.js";
@@ -33,6 +33,19 @@ const getCurrency = (currencyId, methods) => {
33
33
  const getMethod = (methodId, methods) => {
34
34
  return methods.find((x) => x.id === methodId);
35
35
  };
36
+ const syncToSpaceRequest = (userDid, spaceDid) => {
37
+ const cacheKey = `sync-space-${userDid}-${spaceDid}`;
38
+ const cachedRequest = new CachedRequest(cacheKey, () => api.post("/api/customers/sync-to-space"), {
39
+ ttl: 1e3 * 60 * 60
40
+ // 1 hour
41
+ });
42
+ return cachedRequest.fetch(false).then((res) => {
43
+ if (!res.success) {
44
+ cachedRequest.clearCache();
45
+ }
46
+ return res;
47
+ });
48
+ };
36
49
  function PaymentProvider({ session, connect, children, baseUrl, authToken }) {
37
50
  if (baseUrl) {
38
51
  window.__PAYMENT_KIT_BASE_URL = baseUrl;
@@ -56,6 +69,13 @@ function PaymentProvider({ session, connect, children, baseUrl, authToken }) {
56
69
  } = useRequest(getSettings, {
57
70
  refreshDeps: [livemode]
58
71
  });
72
+ useEffect(() => {
73
+ const didSpace = session?.user?.didSpace;
74
+ const userDid = session?.user?.did;
75
+ if (userDid && didSpace && didSpace.endpoint && didSpace.did) {
76
+ syncToSpaceRequest(userDid, didSpace.did);
77
+ }
78
+ }, [session?.user]);
59
79
  const prefix = getPrefix();
60
80
  const [payable, setPayable] = useState(true);
61
81
  if (error) {
@@ -11,7 +11,7 @@ export declare class CachedRequest {
11
11
  private getCache;
12
12
  private getCachedData;
13
13
  private setCachedData;
14
- private clearCache;
14
+ clearCache(): void;
15
15
  fetch(forceRefresh?: boolean): Promise<any>;
16
16
  }
17
17
  export {};
package/es/libs/util.d.ts CHANGED
@@ -120,3 +120,4 @@ export declare function parseMarkedText(text: string): Array<{
120
120
  type: 'text' | 'marked';
121
121
  content: string;
122
122
  }>;
123
+ export declare function getTokenBalanceLink(method: TPaymentMethod, address: string): string;
package/es/libs/util.js CHANGED
@@ -979,3 +979,16 @@ export function parseMarkedText(text) {
979
979
  }
980
980
  return result.filter((p) => p.content !== "");
981
981
  }
982
+ export function getTokenBalanceLink(method, address) {
983
+ if (!method || !address) {
984
+ return "";
985
+ }
986
+ const explorerHost = method?.settings?.[method?.type]?.explorer_host || "";
987
+ if (method.type === "arcblock" && address) {
988
+ return joinURL(explorerHost, "accounts", address, "tokens");
989
+ }
990
+ if (["ethereum", "base"].includes(method.type) && address) {
991
+ return joinURL(explorerHost, "address", address);
992
+ }
993
+ return "";
994
+ }
@@ -1 +1,2 @@
1
1
  export declare function validatePostalCode(postalCode: string, country?: string): boolean;
2
+ export declare function getFieldValidation(fieldName: string, validations?: Record<string, any>, locale?: string): Record<string, any>;
@@ -1,4 +1,5 @@
1
1
  import isPostalCode from "validator/lib/isPostalCode";
2
+ import { t } from "../locales/index.js";
2
3
  const POSTAL_CODE_SUPPORTED_COUNTRIES = [
3
4
  "AD",
4
5
  "AT",
@@ -68,3 +69,16 @@ export function validatePostalCode(postalCode, country) {
68
69
  return false;
69
70
  }
70
71
  }
72
+ export function getFieldValidation(fieldName, validations, locale = "en") {
73
+ if (!validations || !validations[fieldName])
74
+ return {};
75
+ const fieldValidation = validations[fieldName];
76
+ const rules = {};
77
+ if (fieldValidation.pattern) {
78
+ rules.pattern = {
79
+ value: new RegExp(fieldValidation.pattern),
80
+ message: fieldValidation.pattern_message?.[locale] || t("payment.checkout.invalid", locale)
81
+ };
82
+ }
83
+ return rules;
84
+ }
package/es/locales/en.js CHANGED
@@ -232,7 +232,8 @@ export default flat({
232
232
  confirmPrompt: "Please confirm the details before proceeding.",
233
233
  payer: "Account",
234
234
  amount: "Amount",
235
- failed: "Account changed, please pay manually."
235
+ failed: "Account changed, please pay manually.",
236
+ balanceLink: "View Balance"
236
237
  }
237
238
  },
238
239
  customer: {
package/es/locales/zh.js CHANGED
@@ -232,7 +232,8 @@ export default flat({
232
232
  confirmPrompt: "\u8BF7\u786E\u8BA4\u652F\u4ED8\u4FE1\u606F\u65E0\u8BEF\u540E\u7EE7\u7EED\u3002",
233
233
  payer: "\u8D26\u6237\u5730\u5740",
234
234
  amount: "\u652F\u4ED8\u91D1\u989D",
235
- failed: "\u8D26\u6237\u53D1\u751F\u53D8\u5316\uFF0C\u65E0\u6CD5\u81EA\u52A8\u5B8C\u6210\u652F\u4ED8\uFF0C\u8BF7\u624B\u52A8\u652F\u4ED8\u3002"
235
+ failed: "\u8D26\u6237\u53D1\u751F\u53D8\u5316\uFF0C\u65E0\u6CD5\u81EA\u52A8\u5B8C\u6210\u652F\u4ED8\uFF0C\u8BF7\u624B\u52A8\u652F\u4ED8\u3002",
236
+ balanceLink: "\u67E5\u770B\u4F59\u989D"
236
237
  }
237
238
  },
238
239
  customer: {
@@ -3,11 +3,15 @@ type Props = {
3
3
  mode: string;
4
4
  stripe: boolean;
5
5
  sx?: SxProps;
6
+ fieldValidation?: Record<string, any>;
7
+ errorPosition?: 'right' | 'bottom';
6
8
  };
7
- declare function AddressForm({ mode, stripe, sx }: Props): import("react").JSX.Element | null;
9
+ declare function AddressForm({ mode, stripe, sx, fieldValidation, errorPosition }: Props): import("react").JSX.Element | null;
8
10
  declare namespace AddressForm {
9
11
  var defaultProps: {
10
12
  sx: {};
13
+ fieldValidation: {};
14
+ errorPosition: string;
11
15
  };
12
16
  }
13
17
  export default AddressForm;
@@ -4,12 +4,14 @@ import { Fade, FormLabel, InputAdornment, Stack } from "@mui/material";
4
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
+ import { getFieldValidation, validatePostalCode } from "../../libs/validator.js";
8
8
  AddressForm.defaultProps = {
9
- sx: {}
9
+ sx: {},
10
+ fieldValidation: {},
11
+ errorPosition: "right"
10
12
  };
11
- export default function AddressForm({ mode, stripe, sx = {} }) {
12
- const { t } = useLocaleContext();
13
+ export default function AddressForm({ mode, stripe, sx = {}, fieldValidation, errorPosition }) {
14
+ const { t, locale } = useLocaleContext();
13
15
  const { control } = useFormContext();
14
16
  const country = useWatch({ control, name: "billing_address.country" });
15
17
  if (mode === "required") {
@@ -19,8 +21,11 @@ export default function AddressForm({ mode, stripe, sx = {} }) {
19
21
  FormInput,
20
22
  {
21
23
  name: "billing_address.line1",
22
- rules: { required: t("payment.checkout.required") },
23
- errorPosition: "right",
24
+ rules: {
25
+ required: t("payment.checkout.required"),
26
+ ...getFieldValidation("billing_address.line1", fieldValidation, locale)
27
+ },
28
+ errorPosition,
24
29
  variant: "outlined",
25
30
  placeholder: t("payment.checkout.billing.line1")
26
31
  }
@@ -30,8 +35,11 @@ export default function AddressForm({ mode, stripe, sx = {} }) {
30
35
  FormInput,
31
36
  {
32
37
  name: "billing_address.city",
33
- rules: { required: t("payment.checkout.required") },
34
- errorPosition: "right",
38
+ rules: {
39
+ required: t("payment.checkout.required"),
40
+ ...getFieldValidation("billing_address.city", fieldValidation, locale)
41
+ },
42
+ errorPosition,
35
43
  variant: "outlined",
36
44
  placeholder: t("payment.checkout.billing.city")
37
45
  }
@@ -41,8 +49,11 @@ export default function AddressForm({ mode, stripe, sx = {} }) {
41
49
  FormInput,
42
50
  {
43
51
  name: "billing_address.state",
44
- rules: { required: t("payment.checkout.required") },
45
- errorPosition: "right",
52
+ rules: {
53
+ required: t("payment.checkout.required"),
54
+ ...getFieldValidation("billing_address.state", fieldValidation, locale)
55
+ },
56
+ errorPosition,
46
57
  variant: "outlined",
47
58
  placeholder: t("payment.checkout.billing.state")
48
59
  }
@@ -57,9 +68,10 @@ export default function AddressForm({ mode, stripe, sx = {} }) {
57
68
  validate: (x) => {
58
69
  const isValid = validatePostalCode(x, country);
59
70
  return isValid ? true : t("payment.checkout.invalid");
60
- }
71
+ },
72
+ ...getFieldValidation("billing_address.postal_code", fieldValidation, locale)
61
73
  },
62
- errorPosition: "right",
74
+ errorPosition,
63
75
  variant: "outlined",
64
76
  placeholder: t("payment.checkout.billing.postal_code"),
65
77
  InputProps: {
@@ -98,9 +110,10 @@ export default function AddressForm({ mode, stripe, sx = {} }) {
98
110
  validate: (x) => {
99
111
  const isValid = validatePostalCode(x, country);
100
112
  return isValid ? true : t("payment.checkout.invalid");
101
- }
113
+ },
114
+ ...getFieldValidation("billing_address.postal_code", fieldValidation, locale)
102
115
  },
103
- errorPosition: "right",
116
+ errorPosition,
104
117
  variant: "outlined",
105
118
  placeholder: t("payment.checkout.billing.postal_code"),
106
119
  wrapperStyle: { height: "40px" },
@@ -2,7 +2,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  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
- import { Box, Button, CircularProgress, Divider, Fade, FormLabel, Stack, Typography } from "@mui/material";
5
+ import { Box, Button, CircularProgress, Divider, Fade, FormLabel, Stack, Tooltip, Typography } from "@mui/material";
6
6
  import { useMemoizedFn, useSetState } from "ahooks";
7
7
  import pWaitFor from "p-wait-for";
8
8
  import { useEffect, useMemo, useRef } from "react";
@@ -13,7 +13,7 @@ import isEmail from "validator/es/lib/isEmail";
13
13
  import { fromUnitToToken } from "@ocap/util";
14
14
  import DID from "@arcblock/ux/lib/DID";
15
15
  import isEmpty from "lodash/isEmpty";
16
- import { HelpOutline } from "@mui/icons-material";
16
+ import { HelpOutline, OpenInNew } from "@mui/icons-material";
17
17
  import FormInput from "../../components/input.js";
18
18
  import { usePaymentContext } from "../../contexts/payment.js";
19
19
  import { useSubscription } from "../../hooks/subscription.js";
@@ -24,6 +24,7 @@ import {
24
24
  formatQuantityInventory,
25
25
  getPrefix,
26
26
  getStatementDescriptor,
27
+ getTokenBalanceLink,
27
28
  isCrossOrigin
28
29
  } from "../../libs/util.js";
29
30
  import AddressForm from "./address.js";
@@ -36,6 +37,7 @@ import LoadingButton from "../../components/loading-button.js";
36
37
  import OverdueInvoicePayment from "../../components/over-due-invoice-payment.js";
37
38
  import { saveCurrencyPreference } from "../../libs/currency.js";
38
39
  import ConfirmDialog from "../../components/confirm.js";
40
+ import { getFieldValidation } from "../../libs/validator.js";
39
41
  export const waitForCheckoutComplete = async (sessionId) => {
40
42
  let result;
41
43
  await pWaitFor(
@@ -104,6 +106,7 @@ export default function PaymentForm({
104
106
  const { isMobile } = useMobile();
105
107
  const { session, connect, payable } = usePaymentContext();
106
108
  const subscription = useSubscription("events");
109
+ const formErrorPosition = "bottom";
107
110
  const {
108
111
  control,
109
112
  getValues,
@@ -456,6 +459,7 @@ export default function PaymentForm({
456
459
  window.removeEventListener("keydown", handleKeyDown);
457
460
  };
458
461
  }, [state.submitting, state.paying, state.stripePaying, quantityInventoryStatus, payable]);
462
+ const balanceLink = getTokenBalanceLink(method, state.fastCheckoutInfo?.payer || "");
459
463
  const FastCheckoutConfirmDialog = state.fastCheckoutInfo && /* @__PURE__ */ jsx(
460
464
  ConfirmDialog,
461
465
  {
@@ -469,7 +473,23 @@ export default function PaymentForm({
469
473
  /* @__PURE__ */ jsxs(Stack, { spacing: 1, children: [
470
474
  /* @__PURE__ */ jsxs(Stack, { flexDirection: "row", alignItems: "center", justifyContent: "space-between", children: [
471
475
  /* @__PURE__ */ jsx(Typography, { color: "text.primary", sx: { whiteSpace: "nowrap" }, children: t("payment.checkout.fastPay.payer") }),
472
- /* @__PURE__ */ jsx(Typography, { children: /* @__PURE__ */ jsx(DID, { did: state.fastCheckoutInfo.payer || "", compact: true, responsive: false }) })
476
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: 0.5 }, children: [
477
+ /* @__PURE__ */ jsx(DID, { did: state.fastCheckoutInfo.payer || "", compact: true, responsive: false }),
478
+ balanceLink && /* @__PURE__ */ jsx(Tooltip, { title: t("payment.checkout.fastPay.balanceLink"), placement: "top", children: /* @__PURE__ */ jsx(
479
+ OpenInNew,
480
+ {
481
+ sx: {
482
+ color: "text.lighter",
483
+ fontSize: "0.85rem",
484
+ cursor: "pointer",
485
+ "&:hover": { color: "text.primary" }
486
+ },
487
+ onClick: () => {
488
+ window.open(balanceLink, "_blank");
489
+ }
490
+ }
491
+ ) })
492
+ ] })
473
493
  ] }),
474
494
  /* @__PURE__ */ jsxs(Stack, { flexDirection: "row", alignItems: "center", justifyContent: "space-between", children: [
475
495
  /* @__PURE__ */ jsx(Typography, { color: "text.primary", children: t("payment.checkout.fastPay.amount") }),
@@ -609,9 +629,10 @@ export default function PaymentForm({
609
629
  {
610
630
  name: "customer_name",
611
631
  variant: "outlined",
612
- errorPosition: "right",
632
+ errorPosition: formErrorPosition,
613
633
  rules: {
614
- required: t("payment.checkout.required")
634
+ required: t("payment.checkout.required"),
635
+ ...getFieldValidation("customer_name", checkoutSession.metadata?.page_info?.field_validation, locale)
615
636
  }
616
637
  }
617
638
  ),
@@ -621,10 +642,15 @@ export default function PaymentForm({
621
642
  {
622
643
  name: "customer_email",
623
644
  variant: "outlined",
624
- errorPosition: "right",
645
+ errorPosition: formErrorPosition,
625
646
  rules: {
626
647
  required: t("payment.checkout.required"),
627
- validate: (x) => isEmail(x) ? true : t("payment.checkout.invalid")
648
+ validate: (x) => isEmail(x) ? true : t("payment.checkout.invalid"),
649
+ ...getFieldValidation(
650
+ "customer_email",
651
+ checkoutSession.metadata?.page_info?.field_validation,
652
+ locale
653
+ )
628
654
  }
629
655
  }
630
656
  ),
@@ -635,14 +661,19 @@ export default function PaymentForm({
635
661
  {
636
662
  name: "customer_phone",
637
663
  variant: "outlined",
638
- errorPosition: "right",
664
+ errorPosition: formErrorPosition,
639
665
  placeholder: "Phone number",
640
666
  rules: {
641
667
  required: t("payment.checkout.required"),
642
668
  validate: async (x) => {
643
669
  const isValid = await validatePhoneNumber(x);
644
670
  return isValid ? true : t("payment.checkout.invalid");
645
- }
671
+ },
672
+ ...getFieldValidation(
673
+ "customer_phone",
674
+ checkoutSession.metadata?.page_info?.field_validation,
675
+ locale
676
+ )
646
677
  }
647
678
  }
648
679
  )
@@ -652,7 +683,9 @@ export default function PaymentForm({
652
683
  {
653
684
  mode: checkoutSession.billing_address_collection,
654
685
  stripe: method?.type === "stripe",
655
- sx: { marginTop: "0 !important" }
686
+ sx: { marginTop: "0 !important" },
687
+ fieldValidation: checkoutSession.metadata?.page_info?.field_validation,
688
+ errorPosition: formErrorPosition
656
689
  }
657
690
  )
658
691
  ]
@@ -82,7 +82,8 @@ export default function PhoneInput({ ...props }) {
82
82
  "&.Mui-focused .MuiOutlinedInput-notchedOutline": {
83
83
  borderColor: "transparent"
84
84
  }
85
- }
85
+ },
86
+ showDialCode: true
86
87
  }
87
88
  ) })
88
89
  },
@@ -10,6 +10,7 @@ import { useMobile } from "../../../hooks/mobile.js";
10
10
  import LoadingButton from "../../../components/loading-button.js";
11
11
  const { Elements, PaymentElement, useElements, useStripe, loadStripe, LinkAuthenticationElement } = globalThis.__STRIPE_COMPONENTS__;
12
12
  const PaymentElementContainer = styled("div")`
13
+ width: 100%;
13
14
  opacity: 0;
14
15
  transition: opacity 300ms ease;
15
16
  &.visible {
@@ -5,6 +5,7 @@ export type CountrySelectProps = {
5
5
  onChange: (value: CountryIso2) => void;
6
6
  name: string;
7
7
  sx?: SxProps;
8
+ showDialCode?: boolean;
8
9
  };
9
10
  declare const CountrySelect: import("react").ForwardRefExoticComponent<CountrySelectProps & import("react").RefAttributes<HTMLDivElement>>;
10
11
  export default CountrySelect;