@blocklet/payment-react 1.18.33 → 1.18.34

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.
@@ -13,8 +13,11 @@ import { Controller, useFormContext, useWatch } from 'react-hook-form';
13
13
  import { joinURL } from 'ufo';
14
14
  import { dispatch } from 'use-bus';
15
15
  import isEmail from 'validator/es/lib/isEmail';
16
+ import { fromUnitToToken } from '@ocap/util';
17
+ import DID from '@arcblock/ux/lib/DID';
16
18
 
17
19
  import isEmpty from 'lodash/isEmpty';
20
+ import { HelpOutline } from '@mui/icons-material';
18
21
  import FormInput from '../../components/input';
19
22
  import { usePaymentContext } from '../../contexts/payment';
20
23
  import { useSubscription } from '../../hooks/subscription';
@@ -37,6 +40,7 @@ import { formatPhone, validatePhoneNumber } from '../../libs/phone-validator';
37
40
  import LoadingButton from '../../components/loading-button';
38
41
  import OverdueInvoicePayment from '../../components/over-due-invoice-payment';
39
42
  import { saveCurrencyPreference } from '../../libs/currency';
43
+ import ConfirmDialog from '../../components/confirm';
40
44
 
41
45
  export const waitForCheckoutComplete = async (sessionId: string) => {
42
46
  let result: CheckoutContext;
@@ -101,6 +105,14 @@ type UserInfo = {
101
105
  };
102
106
  };
103
107
 
108
+ type FastCheckoutInfo = {
109
+ open: boolean;
110
+ loading: boolean;
111
+ sourceType: 'balance' | 'delegation';
112
+ amount: string;
113
+ payer?: string;
114
+ };
115
+
104
116
  const setUserFormValues = (
105
117
  userInfo: UserInfo,
106
118
  currentValues: any,
@@ -159,7 +171,7 @@ export default function PaymentForm({
159
171
  isDonation = false,
160
172
  }: PageData) {
161
173
  // const theme = useTheme();
162
- const { t } = useLocaleContext();
174
+ const { t, locale } = useLocaleContext();
163
175
  const { isMobile } = useMobile();
164
176
  const { session, connect, payable } = usePaymentContext();
165
177
  const subscription = useSubscription('events');
@@ -194,6 +206,7 @@ export default function PaymentForm({
194
206
  customer?: TCustomer;
195
207
  customerLimited?: boolean;
196
208
  stripePaying: boolean;
209
+ fastCheckoutInfo: FastCheckoutInfo | null;
197
210
  }>({
198
211
  submitting: false,
199
212
  paying: false,
@@ -203,6 +216,7 @@ export default function PaymentForm({
203
216
  customer,
204
217
  customerLimited: false,
205
218
  stripePaying: false,
219
+ fastCheckoutInfo: null,
206
220
  });
207
221
 
208
222
  const currencies = flattenPaymentMethods(paymentMethods);
@@ -288,6 +302,7 @@ export default function PaymentForm({
288
302
  }, [session?.user, checkoutSession.phone_number_collection?.enabled]);
289
303
 
290
304
  const paymentMethod = useWatch({ control, name: 'payment_method' });
305
+ const paymentCurrencyId = useWatch({ control, name: 'payment_currency' });
291
306
 
292
307
  // const domSize = useSize(document.body);
293
308
 
@@ -327,6 +342,9 @@ export default function PaymentForm({
327
342
  buttonText = session?.user || isDonation ? buttonText : t('payment.checkout.connect', { action: buttonText });
328
343
 
329
344
  const method = paymentMethods.find((x) => x.id === paymentMethod) as TPaymentMethodExpanded;
345
+ const paymentCurrency = currencies.find((x) => x.id === paymentCurrencyId);
346
+ const showStake = method.type === 'arcblock' && !checkoutSession.subscription_data?.no_stake;
347
+
330
348
  const isDonationMode = checkoutSession?.submit_type === 'donate' && isDonation;
331
349
  const showForm = !!session?.user;
332
350
  const skipBindWallet = method.type === 'stripe';
@@ -364,6 +382,84 @@ export default function PaymentForm({
364
382
  }
365
383
  };
366
384
 
385
+ const handleFastCheckoutConfirm = async () => {
386
+ if (!state.fastCheckoutInfo) return;
387
+
388
+ setState({
389
+ fastCheckoutInfo: {
390
+ ...state.fastCheckoutInfo,
391
+ loading: true,
392
+ },
393
+ });
394
+
395
+ try {
396
+ const result = await api.post(`/api/checkout-sessions/${checkoutSession.id}/fast-checkout-confirm`);
397
+ if (result.data.fastPaid) {
398
+ setState({
399
+ fastCheckoutInfo: null,
400
+ paying: true,
401
+ });
402
+ await handleConnected();
403
+ } else {
404
+ Toast.error(t('payment.checkout.fastPay.failed'));
405
+ setState({
406
+ fastCheckoutInfo: null,
407
+ paying: true,
408
+ });
409
+ openConnect();
410
+ }
411
+ } catch (err) {
412
+ console.error(err);
413
+ Toast.error(formatError(err));
414
+ setState({
415
+ fastCheckoutInfo: null,
416
+ });
417
+ }
418
+ };
419
+
420
+ const handleFastCheckoutCancel = () => {
421
+ setState({ fastCheckoutInfo: null });
422
+ };
423
+
424
+ const openConnect = () => {
425
+ try {
426
+ if (!['arcblock', 'ethereum', 'base'].includes(method.type)) {
427
+ return;
428
+ }
429
+ setState({ paying: true });
430
+ connect.open({
431
+ locale,
432
+ containerEl: undefined as unknown as Element,
433
+ action: checkoutSession.mode,
434
+ prefix: joinURL(getPrefix(), '/api/did'),
435
+ saveConnect: false,
436
+ useSocket: isCrossOrigin() === false,
437
+ extraParams: { checkoutSessionId: checkoutSession.id, sessionUserDid: session?.user?.did },
438
+ onSuccess: async () => {
439
+ connect.close();
440
+ await handleConnected();
441
+ },
442
+ onClose: () => {
443
+ connect.close();
444
+ setState({ submitting: false, paying: false });
445
+ },
446
+ onError: (err: any) => {
447
+ console.error(err);
448
+ setState({ submitting: false, paying: false });
449
+ onError(err);
450
+ },
451
+ messages: {
452
+ title: t('payment.checkout.connectModal.title', { action: buttonText }),
453
+ scan: t('payment.checkout.connectModal.scan'),
454
+ confirm: t('payment.checkout.connectModal.confirm'),
455
+ cancel: t('payment.checkout.connectModal.cancel'),
456
+ },
457
+ } as any);
458
+ } catch (err) {
459
+ Toast.error(formatError(err));
460
+ }
461
+ };
462
+
367
463
  const onFormSubmit = async (data: any) => {
368
464
  setState({ submitting: true });
369
465
  try {
@@ -383,36 +479,22 @@ export default function PaymentForm({
383
479
  });
384
480
 
385
481
  if (['arcblock', 'ethereum', 'base'].includes(method.type)) {
386
- setState({ paying: true });
387
- if ((result.data.balance?.sufficient || result.data.delegation?.sufficient) && !isDonationMode) {
388
- await handleConnected();
389
- } else {
390
- connect.open({
391
- containerEl: undefined as unknown as Element,
392
- action: checkoutSession.mode,
393
- prefix: joinURL(getPrefix(), '/api/did'),
394
- saveConnect: false,
395
- useSocket: isCrossOrigin() === false,
396
- extraParams: { checkoutSessionId: checkoutSession.id, sessionUserDid: session?.user?.did },
397
- onSuccess: async () => {
398
- connect.close();
399
- await handleConnected();
482
+ if (
483
+ (result.data.balance?.sufficient || result.data.delegation?.sufficient) &&
484
+ !isDonationMode &&
485
+ result.data.fastPayInfo
486
+ ) {
487
+ setState({
488
+ fastCheckoutInfo: {
489
+ open: true,
490
+ loading: false,
491
+ sourceType: result.data.fastPayInfo.type,
492
+ amount: result.data.fastPayInfo.amount,
493
+ payer: result.data.fastPayInfo.payer,
400
494
  },
401
- onClose: () => {
402
- connect.close();
403
- setState({ submitting: false, paying: false });
404
- },
405
- onError: (err: any) => {
406
- console.error(err);
407
- setState({ submitting: false, paying: false });
408
- onError(err);
409
- },
410
- messages: {
411
- title: 'DID Connect',
412
- scan: 'Use following methods to complete this payment',
413
- confirm: 'Confirm',
414
- },
415
- } as any);
495
+ });
496
+ } else {
497
+ openConnect();
416
498
  }
417
499
  }
418
500
  if (['stripe'].includes(method.type)) {
@@ -514,6 +596,40 @@ export default function PaymentForm({
514
596
  };
515
597
  }, [state.submitting, state.paying, state.stripePaying, quantityInventoryStatus, payable]); // eslint-disable-line react-hooks/exhaustive-deps
516
598
 
599
+ const FastCheckoutConfirmDialog = state.fastCheckoutInfo && (
600
+ <ConfirmDialog
601
+ onConfirm={handleFastCheckoutConfirm}
602
+ onCancel={handleFastCheckoutCancel}
603
+ title={t('payment.checkout.fastPay.title')}
604
+ message={
605
+ <Stack>
606
+ <Typography>{t('payment.checkout.fastPay.autoPaymentReason')}</Typography>
607
+ <Typography>{t('payment.checkout.fastPay.confirmPrompt')}</Typography>
608
+ <Divider sx={{ mt: 1.5, mb: 1.5 }} />
609
+ <Stack spacing={1}>
610
+ <Stack flexDirection="row" alignItems="center" justifyContent="space-between">
611
+ <Typography color="text.primary" sx={{ whiteSpace: 'nowrap' }}>
612
+ {t('payment.checkout.fastPay.payer')}
613
+ </Typography>
614
+ <Typography>
615
+ <DID did={state.fastCheckoutInfo.payer || ''} compact responsive={false} />
616
+ </Typography>
617
+ </Stack>
618
+ <Stack flexDirection="row" alignItems="center" justifyContent="space-between">
619
+ <Typography color="text.primary">{t('payment.checkout.fastPay.amount')}</Typography>
620
+ <Typography>
621
+ {fromUnitToToken(state.fastCheckoutInfo.amount, paymentCurrency?.decimal || 18).toString()}{' '}
622
+ {paymentCurrency?.symbol}
623
+ </Typography>
624
+ </Stack>
625
+ </Stack>
626
+ </Stack>
627
+ }
628
+ loading={state.fastCheckoutInfo.loading}
629
+ color="primary"
630
+ />
631
+ );
632
+
517
633
  if (onlyShowBtn) {
518
634
  return (
519
635
  <>
@@ -565,6 +681,7 @@ export default function PaymentForm({
565
681
  }}
566
682
  />
567
683
  )}
684
+ {FastCheckoutConfirmDialog}
568
685
  </>
569
686
  );
570
687
  }
@@ -695,9 +812,21 @@ export default function PaymentForm({
695
812
 
696
813
  {['subscription', 'setup'].includes(checkoutSession.mode) && (
697
814
  <Typography sx={{ mt: 2.5, color: 'text.lighter', fontSize: '0.7875rem', lineHeight: '0.9625rem' }}>
698
- {t('payment.checkout.confirm', { payee })}
815
+ {showStake
816
+ ? t('payment.checkout.confirm.withStake', { payee })
817
+ : t('payment.checkout.confirm.withoutStake', { payee })}
699
818
  </Typography>
700
819
  )}
820
+ {checkoutSession.metadata?.page_info?.form_purpose_description && (
821
+ <Box sx={{ mt: 1, display: 'flex', alignItems: 'center', gap: 0.5 }}>
822
+ <HelpOutline sx={{ color: 'text.lighter', fontSize: '0.75rem' }} />
823
+ <Typography variant="body2" sx={{ fontSize: '0.75rem', color: 'text.lighter' }}>
824
+ {locale === 'zh'
825
+ ? checkoutSession.metadata.page_info.form_purpose_description.zh
826
+ : checkoutSession.metadata.page_info.form_purpose_description.en}
827
+ </Typography>
828
+ </Box>
829
+ )}
701
830
  </Stack>
702
831
  </Fade>
703
832
  {state.customerLimited && (
@@ -725,6 +854,7 @@ export default function PaymentForm({
725
854
  }}
726
855
  />
727
856
  )}
857
+ {FastCheckoutConfirmDialog}
728
858
  </>
729
859
  );
730
860
  }