@blocklet/payment-react 1.21.13 → 1.21.14

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,50 @@ 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.postal_code')}</FormLabel>
106
+ <Stack direction="row" spacing={0}>
107
+ <FormInput
108
+ name="billing_address.postal_code"
109
+ rules={{
110
+ required: t('payment.checkout.required'),
111
+ validate: (x: string) => {
112
+ const isValid = validatePostalCode(x, country);
113
+ return isValid ? true : t('payment.checkout.invalid');
114
+ },
115
+ ...getFieldValidation('billing_address.postal_code', fieldValidation, locale),
116
+ }}
117
+ errorPosition={errorPosition}
118
+ variant="outlined"
119
+ placeholder={t('payment.checkout.billing.postal_code')}
120
+ InputProps={{
121
+ startAdornment: (
122
+ <InputAdornment position="start" style={{ marginRight: '2px', marginLeft: '-8px' }}>
123
+ <Controller
124
+ name="billing_address.country"
125
+ control={control}
126
+ render={({ field }) => (
127
+ <CountrySelect
128
+ {...field}
129
+ ref={field.ref as unknown as React.RefObject<HTMLDivElement | null>}
130
+ sx={{
131
+ '.MuiOutlinedInput-notchedOutline': {
132
+ borderColor: 'transparent !important',
133
+ },
134
+ }}
135
+ />
136
+ )}
137
+ />
138
+ </InputAdornment>
139
+ ),
140
+ }}
141
+ />
145
142
  </Stack>
146
143
  </Stack>
147
- </Fade>
148
- );
149
- }
150
-
151
- return null;
144
+ </Stack>
145
+ </Fade>
146
+ );
152
147
  }
@@ -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,110 @@ 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
+ }
384
399
  }
385
- if (session?.user?.fullName && session?.user?.email) {
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)) {
386
410
  return false;
387
411
  }
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 (checkoutSession.phone_number_collection?.enabled) {
422
+ if (!phone || phone.trim() === '') {
423
+ return false;
424
+ }
425
+ const phoneValidation = getFieldValidation('customer_phone', fieldValidation, locale);
426
+ if (phoneValidation.pattern) {
427
+ const pattern = phoneValidation.pattern.value;
428
+ if (!pattern.test(phone)) {
429
+ return false;
430
+ }
431
+ }
432
+ }
433
+
434
+ const addressMode = checkoutSession.billing_address_collection;
435
+ if (addressMode === 'required') {
436
+ if (!address?.country || !address?.province || !address?.line1 || !address?.city || !address?.postalCode) {
437
+ return false;
438
+ }
439
+
440
+ const line1Validation = getFieldValidation('billing_address.line1', fieldValidation, locale);
441
+ if (line1Validation.pattern) {
442
+ const pattern = line1Validation.pattern.value;
443
+ if (!pattern.test(address.line1)) {
444
+ return false;
445
+ }
446
+ }
447
+
448
+ const cityValidation = getFieldValidation('billing_address.city', fieldValidation, locale);
449
+ if (cityValidation.pattern) {
450
+ const pattern = cityValidation.pattern.value;
451
+ if (!pattern.test(address.city)) {
452
+ return false;
453
+ }
454
+ }
455
+
456
+ const stateValidation = getFieldValidation('billing_address.state', fieldValidation, locale);
457
+ if (stateValidation.pattern) {
458
+ const pattern = stateValidation.pattern.value;
459
+ if (!pattern.test(address.province)) {
460
+ return false;
461
+ }
462
+ }
463
+ }
464
+
388
465
  return true;
389
- }, [session?.user, method, checkoutSession]);
466
+ };
467
+
468
+ const showForm = useMemo(() => {
469
+ if (!session?.user) {
470
+ return false;
471
+ }
472
+ if (state.showEditForm) {
473
+ return true;
474
+ }
475
+ return !validateUserInfo();
476
+ // eslint-disable-next-line react-hooks/exhaustive-deps
477
+ }, [session?.user, method, state.showEditForm]);
390
478
 
391
479
  const handleConnected = async () => {
392
480
  if (processingRef.current) {
@@ -910,6 +998,64 @@ export default function PaymentForm({
910
998
  )}
911
999
  </Stack>
912
1000
  </Fade>
1001
+ {!showForm && session?.user && (
1002
+ <Stack
1003
+ spacing={1.25}
1004
+ sx={{
1005
+ mt: 2,
1006
+ p: 2,
1007
+ pt: 1,
1008
+ backgroundColor: 'background.paper',
1009
+ borderRadius: 1,
1010
+ border: '1px solid',
1011
+ borderColor: 'divider',
1012
+ }}>
1013
+ <Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ mb: 0.25 }}>
1014
+ <Typography variant="subtitle2" sx={{ color: 'text.primary', fontSize: '0.875rem' }}>
1015
+ {t('payment.checkout.customerInfo')}
1016
+ </Typography>
1017
+ <Button
1018
+ size="small"
1019
+ variant="text"
1020
+ onClick={() => setState({ showEditForm: true })}
1021
+ sx={{ minWidth: 0 }}>
1022
+ {t('common.edit')}
1023
+ </Button>
1024
+ </Stack>
1025
+ <Stack spacing={0.5}>
1026
+ <Typography variant="body2" sx={{ color: 'text.primary', fontWeight: 600, fontSize: '0.9375rem' }}>
1027
+ {session.user.fullName || session.user.name}
1028
+ </Typography>
1029
+ <Typography variant="body2" sx={{ color: 'text.secondary', fontSize: '0.8125rem' }}>
1030
+ {session.user.email}
1031
+ </Typography>
1032
+ {checkoutSession.phone_number_collection?.enabled && session.user.phone && (
1033
+ <Typography variant="body2" sx={{ color: 'text.secondary', fontSize: '0.8125rem' }}>
1034
+ {session.user.phone}
1035
+ </Typography>
1036
+ )}
1037
+ {session.user.address && (
1038
+ <Stack direction="row" alignItems="center" spacing={0.75}>
1039
+ {session.user.address.country && (
1040
+ <FlagEmoji iso2={session.user.address.country.toLowerCase()} style={{ width: 18, height: 14 }} />
1041
+ )}
1042
+ <Typography variant="body2" sx={{ color: 'text.secondary', fontSize: '0.8125rem' }}>
1043
+ {[
1044
+ session.user.address.line1,
1045
+ session.user.address.city,
1046
+ session.user.address.province,
1047
+ session.user.address.country?.toUpperCase(),
1048
+ ]
1049
+ .filter(Boolean)
1050
+ .join(', ')}
1051
+ {session.user.address.postalCode &&
1052
+ ` [ ${t('payment.checkout.billing.postal_code')}: ${session.user.address.postalCode} ]`}
1053
+ </Typography>
1054
+ </Stack>
1055
+ )}
1056
+ </Stack>
1057
+ </Stack>
1058
+ )}
913
1059
  {showForm && (
914
1060
  <Stack
915
1061
  direction="column"
@@ -968,7 +1114,6 @@ export default function PaymentForm({
968
1114
  )}
969
1115
  <AddressForm
970
1116
  mode={checkoutSession.billing_address_collection as string}
971
- stripe={method?.type === 'stripe'}
972
1117
  sx={{ marginTop: '0 !important' }}
973
1118
  fieldValidation={checkoutSession.metadata?.page_info?.field_validation}
974
1119
  errorPosition={formErrorPosition}