@blocklet/payment-react 1.21.13 → 1.21.15

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.
@@ -9,13 +9,12 @@ import { getFieldValidation, validatePostalCode } from '../../libs/validator';
9
9
 
10
10
  type Props = {
11
11
  mode: string;
12
- stripe: boolean;
13
12
  sx?: SxProps;
14
13
  fieldValidation?: Record<string, any>;
15
14
  errorPosition?: 'right' | 'bottom';
16
15
  };
17
16
 
18
- export default function AddressForm({ mode, stripe, sx = {}, fieldValidation = {}, errorPosition = 'right' }: Props) {
17
+ export default function AddressForm({ mode, sx = {}, fieldValidation = {}, errorPosition = 'right' }: Props) {
19
18
  const { t, locale } = useLocaleContext();
20
19
  const { control } = useFormContext();
21
20
  const country = useWatch({ control, name: 'billing_address.country' });
@@ -99,54 +98,61 @@ export default function AddressForm({ mode, stripe, sx = {}, fieldValidation = {
99
98
  );
100
99
  }
101
100
 
102
- if (stripe) {
103
- return (
104
- <Fade in>
105
- <Stack className="cko-payment-address cko-payment-form" sx={sx}>
106
- <Stack direction="column" className="cko-payment-form" spacing={0}>
107
- <FormLabel className="base-label">{t('payment.checkout.billing.postal_code')}</FormLabel>
108
- <Stack direction="row" spacing={0}>
109
- <FormInput
110
- name="billing_address.postal_code"
111
- rules={{
112
- required: t('payment.checkout.required'),
113
- validate: (x: string) => {
114
- const isValid = validatePostalCode(x, country);
115
- return isValid ? true : t('payment.checkout.invalid');
116
- },
117
- ...getFieldValidation('billing_address.postal_code', fieldValidation, locale),
118
- }}
119
- errorPosition={errorPosition}
120
- variant="outlined"
121
- placeholder={t('payment.checkout.billing.postal_code')}
122
- InputProps={{
123
- startAdornment: (
124
- <InputAdornment position="start" style={{ marginRight: '2px', marginLeft: '-8px' }}>
125
- <Controller
126
- name="billing_address.country"
127
- control={control}
128
- render={({ field }) => (
129
- <CountrySelect
130
- {...field}
131
- ref={field.ref as unknown as React.RefObject<HTMLDivElement | null>}
132
- sx={{
133
- '.MuiOutlinedInput-notchedOutline': {
134
- borderColor: 'transparent !important',
135
- },
136
- }}
137
- />
138
- )}
139
- />
140
- </InputAdornment>
141
- ),
142
- }}
143
- />
144
- </Stack>
101
+ return (
102
+ <Fade in>
103
+ <Stack className="cko-payment-address cko-payment-form" sx={sx}>
104
+ <Stack direction="column" className="cko-payment-form" spacing={0}>
105
+ <FormLabel className="base-label">{t('payment.checkout.billing.state')}</FormLabel>
106
+ <FormInput
107
+ name="billing_address.state"
108
+ rules={{
109
+ required: t('payment.checkout.required'),
110
+ ...getFieldValidation('billing_address.state', fieldValidation, locale),
111
+ }}
112
+ errorPosition={errorPosition}
113
+ variant="outlined"
114
+ placeholder={t('payment.checkout.billing.state')}
115
+ />
116
+ <FormLabel className="base-label">{t('payment.checkout.billing.postal_code')}</FormLabel>
117
+ <Stack direction="row" spacing={0}>
118
+ <FormInput
119
+ name="billing_address.postal_code"
120
+ rules={{
121
+ required: t('payment.checkout.required'),
122
+ validate: (x: string) => {
123
+ const isValid = validatePostalCode(x, country);
124
+ return isValid ? true : t('payment.checkout.invalid');
125
+ },
126
+ ...getFieldValidation('billing_address.postal_code', fieldValidation, locale),
127
+ }}
128
+ errorPosition={errorPosition}
129
+ variant="outlined"
130
+ placeholder={t('payment.checkout.billing.postal_code')}
131
+ InputProps={{
132
+ startAdornment: (
133
+ <InputAdornment position="start" style={{ marginRight: '2px', marginLeft: '-8px' }}>
134
+ <Controller
135
+ name="billing_address.country"
136
+ control={control}
137
+ render={({ field }) => (
138
+ <CountrySelect
139
+ {...field}
140
+ ref={field.ref as unknown as React.RefObject<HTMLDivElement | null>}
141
+ sx={{
142
+ '.MuiOutlinedInput-notchedOutline': {
143
+ borderColor: 'transparent !important',
144
+ },
145
+ }}
146
+ />
147
+ )}
148
+ />
149
+ </InputAdornment>
150
+ ),
151
+ }}
152
+ />
145
153
  </Stack>
146
154
  </Stack>
147
- </Fade>
148
- );
149
- }
150
-
151
- return null;
155
+ </Stack>
156
+ </Fade>
157
+ );
152
158
  }
@@ -2,6 +2,7 @@
2
2
  import 'react-international-phone/style.css';
3
3
 
4
4
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
5
+ import { FlagEmoji } from 'react-international-phone';
5
6
  // import { useTheme } from '@arcblock/ux/lib/Theme';
6
7
  import Toast from '@arcblock/ux/lib/Toast';
7
8
  import type {
@@ -52,7 +53,7 @@ import LoadingButton from '../../components/loading-button';
52
53
  import OverdueInvoicePayment from '../../components/over-due-invoice-payment';
53
54
  import { saveCurrencyPreference } from '../../libs/currency';
54
55
  import ConfirmDialog from '../../components/confirm';
55
- import { getFieldValidation } from '../../libs/validator';
56
+ import { getFieldValidation, validatePostalCode } from '../../libs/validator';
56
57
 
57
58
  export const waitForCheckoutComplete = async (sessionId: string) => {
58
59
  let result: CheckoutContext;
@@ -222,6 +223,7 @@ export default function PaymentForm({
222
223
  creditInsufficientInfo: {
223
224
  open: boolean;
224
225
  } | null;
226
+ showEditForm: boolean;
225
227
  }>({
226
228
  submitting: false,
227
229
  paying: false,
@@ -233,6 +235,7 @@ export default function PaymentForm({
233
235
  stripePaying: false,
234
236
  fastCheckoutInfo: null,
235
237
  creditInsufficientInfo: null,
238
+ showEditForm: false,
236
239
  });
237
240
 
238
241
  const currencies = flattenPaymentMethods(paymentMethods);
@@ -368,25 +371,113 @@ export default function PaymentForm({
368
371
  const showStake = method.type === 'arcblock' && !checkoutSession.subscription_data?.no_stake;
369
372
 
370
373
  const isDonationMode = checkoutSession?.submit_type === 'donate' && isDonation;
371
- const showForm = useMemo(() => {
374
+
375
+ const validateUserInfo = () => {
372
376
  if (!session?.user) {
373
377
  return false;
374
378
  }
375
- if (method.type === 'stripe') {
376
- return true;
379
+
380
+ const { fullName, name, email, phone, address } = session.user;
381
+ const fieldValidation = checkoutSession.metadata?.page_info?.field_validation;
382
+
383
+ const hasName = !!(fullName || name);
384
+ if (!hasName) {
385
+ return false;
377
386
  }
378
- if (checkoutSession.phone_number_collection?.enabled) {
379
- return true;
387
+
388
+ const hasValidEmail = email && isEmail(email);
389
+ if (!hasValidEmail) {
390
+ return false;
380
391
  }
381
- const mode = checkoutSession.billing_address_collection;
382
- if (mode === 'required') {
383
- return true;
392
+
393
+ const nameValidation = getFieldValidation('customer_name', fieldValidation, locale);
394
+ if (nameValidation.pattern && fullName) {
395
+ const pattern = nameValidation.pattern.value;
396
+ if (!pattern.test(fullName)) {
397
+ return false;
398
+ }
399
+ }
400
+
401
+ const emailValidation = getFieldValidation('customer_email', fieldValidation, locale);
402
+ if (emailValidation.pattern && email) {
403
+ const pattern = emailValidation.pattern.value;
404
+ if (!pattern.test(email)) {
405
+ return false;
406
+ }
407
+ }
408
+
409
+ if (!address || !address.postalCode || !validatePostalCode(address.postalCode, address.country)) {
410
+ return false;
384
411
  }
385
- if (session?.user?.fullName && session?.user?.email) {
412
+
413
+ const postalCodeValidation = getFieldValidation('billing_address.postal_code', fieldValidation, locale);
414
+ if (postalCodeValidation.pattern) {
415
+ const pattern = postalCodeValidation.pattern.value;
416
+ if (!pattern.test(address.postalCode)) {
417
+ return false;
418
+ }
419
+ }
420
+
421
+ if (!address.province) {
386
422
  return false;
387
423
  }
424
+ const stateValidation = getFieldValidation('billing_address.state', fieldValidation, locale);
425
+ if (stateValidation.pattern) {
426
+ const pattern = stateValidation.pattern.value;
427
+ if (!pattern.test(address.province)) {
428
+ return false;
429
+ }
430
+ }
431
+
432
+ if (checkoutSession.phone_number_collection?.enabled) {
433
+ if (!phone || phone.trim() === '') {
434
+ return false;
435
+ }
436
+ const phoneValidation = getFieldValidation('customer_phone', fieldValidation, locale);
437
+ if (phoneValidation.pattern) {
438
+ const pattern = phoneValidation.pattern.value;
439
+ if (!pattern.test(phone)) {
440
+ return false;
441
+ }
442
+ }
443
+ }
444
+
445
+ const addressMode = checkoutSession.billing_address_collection;
446
+ if (addressMode === 'required') {
447
+ if (!address?.country || !address?.province || !address?.line1 || !address?.city || !address?.postalCode) {
448
+ return false;
449
+ }
450
+
451
+ const line1Validation = getFieldValidation('billing_address.line1', fieldValidation, locale);
452
+ if (line1Validation.pattern) {
453
+ const pattern = line1Validation.pattern.value;
454
+ if (!pattern.test(address.line1)) {
455
+ return false;
456
+ }
457
+ }
458
+
459
+ const cityValidation = getFieldValidation('billing_address.city', fieldValidation, locale);
460
+ if (cityValidation.pattern) {
461
+ const pattern = cityValidation.pattern.value;
462
+ if (!pattern.test(address.city)) {
463
+ return false;
464
+ }
465
+ }
466
+ }
467
+
388
468
  return true;
389
- }, [session?.user, method, checkoutSession]);
469
+ };
470
+
471
+ const showForm = useMemo(() => {
472
+ if (!session?.user) {
473
+ return false;
474
+ }
475
+ if (state.showEditForm) {
476
+ return true;
477
+ }
478
+ return !validateUserInfo();
479
+ // eslint-disable-next-line react-hooks/exhaustive-deps
480
+ }, [session?.user, method, state.showEditForm]);
390
481
 
391
482
  const handleConnected = async () => {
392
483
  if (processingRef.current) {
@@ -910,6 +1001,64 @@ export default function PaymentForm({
910
1001
  )}
911
1002
  </Stack>
912
1003
  </Fade>
1004
+ {!showForm && session?.user && (
1005
+ <Stack
1006
+ spacing={1.25}
1007
+ sx={{
1008
+ mt: 2,
1009
+ p: 2,
1010
+ pt: 1,
1011
+ backgroundColor: 'background.paper',
1012
+ borderRadius: 1,
1013
+ border: '1px solid',
1014
+ borderColor: 'divider',
1015
+ }}>
1016
+ <Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ mb: 0.25 }}>
1017
+ <Typography variant="subtitle2" sx={{ color: 'text.primary', fontSize: '0.875rem' }}>
1018
+ {t('payment.checkout.customerInfo')}
1019
+ </Typography>
1020
+ <Button
1021
+ size="small"
1022
+ variant="text"
1023
+ onClick={() => setState({ showEditForm: true })}
1024
+ sx={{ minWidth: 0 }}>
1025
+ {t('common.edit')}
1026
+ </Button>
1027
+ </Stack>
1028
+ <Stack spacing={0.5}>
1029
+ <Typography variant="body2" sx={{ color: 'text.primary', fontWeight: 600, fontSize: '0.9375rem' }}>
1030
+ {session.user.fullName || session.user.name}
1031
+ </Typography>
1032
+ <Typography variant="body2" sx={{ color: 'text.secondary', fontSize: '0.8125rem' }}>
1033
+ {session.user.email}
1034
+ </Typography>
1035
+ {checkoutSession.phone_number_collection?.enabled && session.user.phone && (
1036
+ <Typography variant="body2" sx={{ color: 'text.secondary', fontSize: '0.8125rem' }}>
1037
+ {session.user.phone}
1038
+ </Typography>
1039
+ )}
1040
+ {session.user.address && (
1041
+ <Stack direction="row" alignItems="center" spacing={0.75}>
1042
+ {session.user.address.country && (
1043
+ <FlagEmoji iso2={session.user.address.country.toLowerCase()} style={{ width: 18, height: 14 }} />
1044
+ )}
1045
+ <Typography variant="body2" sx={{ color: 'text.secondary', fontSize: '0.8125rem' }}>
1046
+ {[
1047
+ session.user.address.line1,
1048
+ session.user.address.city,
1049
+ session.user.address.province,
1050
+ session.user.address.country?.toUpperCase(),
1051
+ ]
1052
+ .filter(Boolean)
1053
+ .join(', ')}
1054
+ {session.user.address.postalCode &&
1055
+ ` [ ${t('payment.checkout.billing.postal_code')}: ${session.user.address.postalCode} ]`}
1056
+ </Typography>
1057
+ </Stack>
1058
+ )}
1059
+ </Stack>
1060
+ </Stack>
1061
+ )}
913
1062
  {showForm && (
914
1063
  <Stack
915
1064
  direction="column"
@@ -968,7 +1117,6 @@ export default function PaymentForm({
968
1117
  )}
969
1118
  <AddressForm
970
1119
  mode={checkoutSession.billing_address_collection as string}
971
- stripe={method?.type === 'stripe'}
972
1120
  sx={{ marginTop: '0 !important' }}
973
1121
  fieldValidation={checkoutSession.metadata?.page_info?.field_validation}
974
1122
  errorPosition={formErrorPosition}