@blocklet/payment-react 1.17.12 → 1.18.1

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 (54) hide show
  1. package/es/checkout/donate.js +40 -10
  2. package/es/checkout/form.d.ts +2 -1
  3. package/es/checkout/form.js +32 -45
  4. package/es/components/payment-beneficiaries.d.ts +24 -0
  5. package/es/components/payment-beneficiaries.js +70 -0
  6. package/es/index.d.ts +2 -1
  7. package/es/index.js +3 -1
  8. package/es/locales/en.js +13 -1
  9. package/es/locales/zh.js +13 -1
  10. package/es/payment/donation-form.d.ts +24 -0
  11. package/es/payment/donation-form.js +603 -0
  12. package/es/payment/error.d.ts +1 -1
  13. package/es/payment/error.js +11 -1
  14. package/es/payment/form/index.d.ts +9 -3
  15. package/es/payment/form/index.js +39 -4
  16. package/es/payment/product-donation.js +98 -57
  17. package/es/payment/skeleton/donation.d.ts +1 -0
  18. package/es/payment/skeleton/donation.js +30 -0
  19. package/es/theme/index.js +3 -0
  20. package/es/types/index.d.ts +2 -0
  21. package/lib/checkout/donate.js +76 -10
  22. package/lib/checkout/form.d.ts +2 -1
  23. package/lib/checkout/form.js +39 -49
  24. package/lib/components/payment-beneficiaries.d.ts +24 -0
  25. package/lib/components/payment-beneficiaries.js +113 -0
  26. package/lib/index.d.ts +2 -1
  27. package/lib/index.js +8 -0
  28. package/lib/locales/en.js +13 -1
  29. package/lib/locales/zh.js +13 -1
  30. package/lib/payment/donation-form.d.ts +24 -0
  31. package/lib/payment/donation-form.js +644 -0
  32. package/lib/payment/error.d.ts +1 -1
  33. package/lib/payment/error.js +2 -2
  34. package/lib/payment/form/index.d.ts +9 -3
  35. package/lib/payment/form/index.js +35 -2
  36. package/lib/payment/product-donation.js +140 -73
  37. package/lib/payment/skeleton/donation.d.ts +1 -0
  38. package/lib/payment/skeleton/donation.js +66 -0
  39. package/lib/theme/index.js +3 -0
  40. package/lib/types/index.d.ts +2 -0
  41. package/package.json +3 -3
  42. package/src/checkout/donate.tsx +54 -11
  43. package/src/checkout/form.tsx +17 -31
  44. package/src/components/payment-beneficiaries.tsx +97 -0
  45. package/src/index.ts +2 -0
  46. package/src/locales/en.tsx +12 -0
  47. package/src/locales/zh.tsx +12 -0
  48. package/src/payment/donation-form.tsx +646 -0
  49. package/src/payment/error.tsx +13 -4
  50. package/src/payment/form/index.tsx +46 -4
  51. package/src/payment/product-donation.tsx +91 -40
  52. package/src/payment/skeleton/donation.tsx +35 -0
  53. package/src/theme/index.tsx +3 -0
  54. package/src/types/index.ts +2 -0
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/indent */
1
2
  import 'react-international-phone/style.css';
2
3
 
3
4
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
@@ -42,7 +43,7 @@ import StripeCheckout from './stripe';
42
43
  import { useMobile } from '../../hooks/mobile';
43
44
  import { validatePhoneNumber } from '../../libs/phone-validator';
44
45
 
45
- const waitForCheckoutComplete = async (sessionId: string) => {
46
+ export const waitForCheckoutComplete = async (sessionId: string) => {
46
47
  let result: CheckoutContext;
47
48
 
48
49
  await pWaitFor(
@@ -71,14 +72,19 @@ const waitForCheckoutComplete = async (sessionId: string) => {
71
72
  return result;
72
73
  };
73
74
 
74
- const hasDidWallet = (user: any) => {
75
+ export const hasDidWallet = (user: any) => {
75
76
  const connected = user?.connectedAccounts || user?.extraConfigs?.connectedAccounts || [];
76
77
  return connected.some((x: any) => x.provider === 'wallet');
77
78
  };
78
79
 
79
- type PageData = CheckoutContext & CheckoutCallbacks;
80
+ type PageData = CheckoutContext &
81
+ CheckoutCallbacks & {
82
+ onlyShowBtn?: boolean;
83
+ };
80
84
 
81
- PaymentForm.defaultProps = {};
85
+ PaymentForm.defaultProps = {
86
+ onlyShowBtn: false,
87
+ };
82
88
 
83
89
  // FIXME: https://stripe.com/docs/elements/address-element
84
90
  // TODO: https://country-regions.github.io/react-country-region-selector/
@@ -96,6 +102,7 @@ export default function PaymentForm({
96
102
  // mode,
97
103
  action,
98
104
  currencyId,
105
+ onlyShowBtn,
99
106
  }: PageData) {
100
107
  // const theme = useTheme();
101
108
  const { t } = useLocaleContext();
@@ -398,6 +405,40 @@ export default function PaymentForm({
398
405
  setState({ stripePaying: false });
399
406
  };
400
407
 
408
+ if (onlyShowBtn) {
409
+ return (
410
+ <>
411
+ <Box className="cko-payment-submit-btn">
412
+ <LoadingButton
413
+ variant="contained"
414
+ color="primary"
415
+ size="large"
416
+ className="cko-submit-button"
417
+ onClick={onAction}
418
+ fullWidth
419
+ disabled={state.submitting || state.paying || state.stripePaying || !quantityInventoryStatus || !payable}
420
+ loading={state.submitting || state.paying}>
421
+ {state.submitting || state.paying ? t('payment.checkout.processing') : buttonText}
422
+ </LoadingButton>
423
+ </Box>
424
+ {state.customerLimited && (
425
+ <ConfirmDialog
426
+ onConfirm={() =>
427
+ window.open(
428
+ joinURL(getPrefix(), `/customer/invoice/past-due?referer=${encodeURIComponent(window.location.href)}`),
429
+ '_self'
430
+ )
431
+ }
432
+ onCancel={() => setState({ customerLimited: false })}
433
+ confirm={t('payment.customer.pastDue.alert.confirm')}
434
+ title={t('payment.customer.pastDue.alert.title')}
435
+ message={t('payment.customer.pastDue.alert.description')}
436
+ color="primary"
437
+ />
438
+ )}
439
+ </>
440
+ );
441
+ }
401
442
  return (
402
443
  <>
403
444
  <Fade in>
@@ -498,6 +539,7 @@ export default function PaymentForm({
498
539
  </Stack>
499
540
  </Fade>
500
541
  <Divider sx={{ mt: 2.5, mb: 2.5 }} />
542
+
501
543
  <Fade in>
502
544
  <Stack className="cko-payment-submit">
503
545
  <Box className="cko-payment-submit-btn">
@@ -1,10 +1,9 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import type { DonationSettings, TLineItemExpanded, TPaymentCurrency } from '@blocklet/payment-types';
3
- import { Box, Card, CardActionArea, FormControlLabel, Stack, TextField, Typography } from '@mui/material';
3
+ import { Avatar, Box, Card, CardActionArea, Grid, Stack, TextField, Typography } from '@mui/material';
4
4
  import { useSetState } from 'ahooks';
5
- import { useEffect } from 'react';
5
+ import { useEffect, useRef } from 'react';
6
6
 
7
- import Switch from '../components/switch-button';
8
7
  import { formatAmountPrecisionLimit } from '../libs/util';
9
8
  import { usePaymentContext } from '../contexts/payment';
10
9
  import { usePreventWheel } from '../hooks/scroll';
@@ -23,13 +22,17 @@ export default function ProductDonation({
23
22
  const { t, locale } = useLocaleContext();
24
23
  const { setPayable } = usePaymentContext();
25
24
  usePreventWheel();
26
- const preset = settings.amount.preset || settings.amount.presets?.[0] || '0';
25
+ const presets = settings?.amount?.presets || [];
26
+ const preset = settings?.amount?.preset || presets?.[0] || '0';
27
+ const supportPreset = presets.length > 0;
28
+ const supportCustom = !!settings?.amount?.custom;
27
29
  const [state, setState] = useSetState({
28
30
  selected: preset,
29
31
  input: '',
30
- custom: settings.amount.presets?.length === 0,
32
+ custom: !supportPreset,
31
33
  error: '',
32
34
  });
35
+ const customInputRef = useRef<HTMLInputElement>(null);
33
36
 
34
37
  // Set default amount
35
38
  useEffect(() => {
@@ -42,6 +45,14 @@ export default function ProductDonation({
42
45
  }
43
46
  }, [settings.amount.preset, settings.amount.presets]); // eslint-disable-line
44
47
 
48
+ useEffect(() => {
49
+ if (state.custom) {
50
+ setTimeout(() => {
51
+ customInputRef.current?.focus();
52
+ }, 0);
53
+ }
54
+ }, [state.custom]);
55
+
45
56
  const handleSelect = (amount: string) => {
46
57
  setState({ selected: amount, custom: false, error: '' });
47
58
  onChange({ priceId: item.price_id, amount });
@@ -69,71 +80,111 @@ export default function ProductDonation({
69
80
  onChange({ priceId: item.price_id, amount: value });
70
81
  };
71
82
 
72
- const handleToggle = (event: any) => {
73
- if (event.target.checked) {
74
- setState({ custom: true, input: state.selected, error: '' });
75
- } else {
76
- setPayable(true);
77
- handleSelect(preset);
78
- }
83
+ const handleCustomSelect = () => {
84
+ setState({ custom: true, error: '' });
79
85
  };
80
86
 
81
87
  return (
82
88
  <Box display="flex" flexDirection="column" alignItems="flex-start" gap={1.5}>
83
- {settings.amount.custom && preset !== '0' && (
84
- <FormControlLabel
85
- control={<Switch checked={state.custom} sx={{ marginRight: 0.4 }} onChange={handleToggle} />}
86
- label={state.custom ? t('payment.checkout.donation.select') : t('payment.checkout.donation.custom')}
87
- sx={{ marginRight: 2, marginLeft: 0.5, color: 'text.lighter' }}
88
- />
89
- )}
90
- {!state.custom && (
91
- <Box display="flex" flexWrap="wrap" alignItems="center" gap={1.5}>
89
+ {supportPreset && (
90
+ <Grid container spacing={2}>
92
91
  {settings.amount.presets &&
93
92
  settings.amount.presets.length > 0 &&
94
93
  settings.amount.presets.map((amount) => (
94
+ <Grid item xs={6} sm={3} key={amount}>
95
+ <Card
96
+ key={amount}
97
+ variant="outlined"
98
+ sx={{
99
+ minWidth: 115,
100
+ textAlign: 'center',
101
+ transition: 'all 0.3s',
102
+ cursor: 'pointer',
103
+ '&:hover': {
104
+ transform: 'translateY(-4px)',
105
+ boxShadow: 3,
106
+ },
107
+ height: '42px',
108
+ ...(state.selected === amount && !state.custom
109
+ ? { borderColor: 'primary.main', borderWidth: 1 }
110
+ : {}),
111
+ }}>
112
+ <CardActionArea onClick={() => handleSelect(amount)}>
113
+ <Stack
114
+ direction="row"
115
+ sx={{ py: 1.5, px: 1.5 }}
116
+ spacing={0.5}
117
+ alignItems="center"
118
+ justifyContent="center">
119
+ <Avatar src={currency?.logo} sx={{ width: 16, height: 16, mr: 0.5 }} alt={currency?.symbol} />
120
+ <Typography
121
+ component="strong"
122
+ lineHeight={1}
123
+ variant="h3"
124
+ sx={{ fontVariantNumeric: 'tabular-nums', fontWeight: 400 }}>
125
+ {amount}
126
+ </Typography>
127
+ <Typography lineHeight={1} fontSize={14} color="text.secondary">
128
+ {currency?.symbol}
129
+ </Typography>
130
+ </Stack>
131
+ </CardActionArea>
132
+ </Card>
133
+ </Grid>
134
+ ))}
135
+ {supportCustom && (
136
+ <Grid item xs={6} sm={3} key="custom">
95
137
  <Card
96
- key={amount}
138
+ key="custom"
97
139
  variant="outlined"
98
140
  sx={{
99
- minWidth: 115,
100
141
  textAlign: 'center',
101
- ...(state.selected === amount && !state.custom ? { borderColor: 'primary.main' } : {}),
142
+ transition: 'all 0.3s',
143
+ cursor: 'pointer',
144
+ '&:hover': {
145
+ transform: 'translateY(-4px)',
146
+ boxShadow: 3,
147
+ },
148
+ height: '42px',
149
+ ...(state.custom ? { borderColor: 'primary.main', borderWidth: 1 } : {}),
102
150
  }}>
103
- <CardActionArea onClick={() => handleSelect(amount)}>
151
+ <CardActionArea onClick={() => handleCustomSelect()}>
104
152
  <Stack
105
153
  direction="row"
106
- sx={{ py: 1, px: 0.5 }}
154
+ sx={{ py: 1.5, px: 1.5 }}
107
155
  spacing={0.5}
108
- alignItems="flex-end"
156
+ alignItems="center"
109
157
  justifyContent="center">
110
- <Typography
111
- component="strong"
112
- lineHeight={1}
113
- variant="h3"
114
- sx={{ fontVariantNumeric: 'tabular-nums', fontWeight: 400 }}>
115
- {amount}
158
+ <Typography variant="h3" lineHeight={1} sx={{ fontWeight: 400 }}>
159
+ {t('common.custom')}
116
160
  </Typography>
117
- <Typography component="small" lineHeight={1} fontSize={12}>
118
- ABT
119
- </Typography>{' '}
120
161
  </Stack>
121
162
  </CardActionArea>
122
163
  </Card>
123
- ))}
124
- </Box>
164
+ </Grid>
165
+ )}
166
+ </Grid>
125
167
  )}
126
168
  {state.custom && (
127
169
  <TextField
128
- label={preset !== '0' ? null : t('payment.checkout.donation.custom')}
129
170
  type="number"
130
171
  value={state.input}
131
172
  onChange={handleInput}
132
- inputProps={{ min: settings.amount.minimum, max: settings.amount.maximum }}
133
173
  margin="none"
134
174
  fullWidth
135
175
  error={!!state.error}
136
176
  helperText={state.error}
177
+ inputRef={customInputRef}
178
+ // eslint-disable-next-line react/jsx-no-duplicate-props
179
+ InputProps={{
180
+ endAdornment: (
181
+ <Stack direction="row" spacing={0.5} alignItems="center" sx={{ ml: 1 }}>
182
+ <Avatar src={currency?.logo} sx={{ width: 16, height: 16 }} alt={currency?.symbol} />
183
+ <Typography>{currency?.symbol}</Typography>
184
+ </Stack>
185
+ ),
186
+ autoComplete: 'off',
187
+ }}
137
188
  sx={{
138
189
  mt: preset !== '0' ? 0 : 1,
139
190
  }}
@@ -0,0 +1,35 @@
1
+ import { Box, Divider, Fade, Skeleton, Stack } from '@mui/material';
2
+
3
+ export default function DonationSkeleton() {
4
+ return (
5
+ <Fade in>
6
+ <Stack direction="column">
7
+ <Skeleton variant="text" sx={{ fontSize: '2rem', width: '40%' }} />
8
+ <Skeleton sx={{ mt: 2 }} variant="rounded" height={80} />
9
+ <Divider
10
+ sx={{
11
+ mt: {
12
+ xs: '16px',
13
+ md: '16px',
14
+ },
15
+ mb: {
16
+ xs: '16px',
17
+ md: '16px',
18
+ },
19
+ }}
20
+ />
21
+ <Stack direction="row" justifyContent="space-between" spacing={2}>
22
+ <Skeleton variant="text" sx={{ fontSize: '1.5rem', width: '40%' }} />
23
+ <Box display="flex" alignItems="center" gap={2}>
24
+ <Box>
25
+ <Skeleton height={60} width={80} />
26
+ </Box>
27
+ <Box>
28
+ <Skeleton height={60} width={120} />
29
+ </Box>
30
+ </Box>
31
+ </Stack>
32
+ </Stack>
33
+ </Fade>
34
+ );
35
+ }
@@ -48,6 +48,9 @@ export function PaymentThemeProvider({
48
48
  minHeight: '1.65em',
49
49
  lineHeight: '1.65em',
50
50
  },
51
+ '&.Mui-focused .MuiOutlinedInput-notchedOutline': {
52
+ borderWidth: '1px',
53
+ },
51
54
  },
52
55
  },
53
56
  },
@@ -46,6 +46,8 @@ export type CheckoutProps = Partial<CheckoutCallbacks> & {
46
46
  action?: string;
47
47
  mode?: LiteralUnion<'standalone' | 'inline' | 'popup' | 'inline-minimal' | 'popup-minimal', string>;
48
48
  theme?: 'default' | 'inherit' | PaymentThemeOptions;
49
+ formType?: 'donation' | 'payment';
50
+ formRender?: Record<string, any>;
49
51
  };
50
52
 
51
53
  export type CheckoutCallbacks = {