@blocklet/payment-react 1.20.5 → 1.20.6

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 (55) hide show
  1. package/.aigne/doc-smith/config.yaml +2 -2
  2. package/.aigne/doc-smith/upload-cache.yaml +381 -0
  3. package/docs/components-business-auto-topup.md +128 -179
  4. package/docs/components-business-overdue-invoice-payment.md +108 -143
  5. package/docs/components-business-resume-subscription.md +117 -104
  6. package/docs/components-business.md +12 -36
  7. package/docs/components-checkout-checkout-donate.md +209 -149
  8. package/docs/components-checkout-checkout-form.md +115 -136
  9. package/docs/components-checkout-checkout-table.md +92 -172
  10. package/docs/components-checkout.md +43 -109
  11. package/docs/components-history-credit-grants-list.md +45 -70
  12. package/docs/components-history-credit-transactions-list.md +57 -67
  13. package/docs/components-history-invoice-list.md +58 -52
  14. package/docs/components-history-payment-list.md +19 -40
  15. package/docs/components-history.md +42 -67
  16. package/docs/components-ui-form-elements-address-form.md +37 -65
  17. package/docs/components-ui-form-elements-country-select.md +80 -59
  18. package/docs/components-ui-form-elements-currency-selector.md +57 -73
  19. package/docs/components-ui-form-elements-phone-input.md +90 -112
  20. package/docs/components-ui-form-elements.md +46 -80
  21. package/docs/components-ui-payment-summary.md +71 -119
  22. package/docs/components-ui-pricing-table.md +117 -204
  23. package/docs/components-ui.md +59 -32
  24. package/docs/components.md +89 -62
  25. package/docs/getting-started.md +36 -63
  26. package/docs/guides-theming.md +122 -84
  27. package/docs/guides-utilities.md +107 -145
  28. package/docs/guides.md +7 -84
  29. package/docs/hooks-use-mobile.md +50 -36
  30. package/docs/hooks-use-subscription.md +72 -89
  31. package/docs/hooks.md +12 -82
  32. package/docs/overview.md +45 -52
  33. package/docs/providers-donate-provider.md +73 -95
  34. package/docs/providers-payment-provider.md +115 -169
  35. package/docs/providers.md +27 -86
  36. package/es/locales/en.js +7 -0
  37. package/es/locales/zh.js +8 -1
  38. package/es/payment/index.js +3 -0
  39. package/es/payment/progress-item.d.ts +12 -0
  40. package/es/payment/progress-item.js +78 -0
  41. package/es/payment/success.d.ts +4 -1
  42. package/es/payment/success.js +55 -3
  43. package/lib/locales/en.js +7 -0
  44. package/lib/locales/zh.js +8 -1
  45. package/lib/payment/index.js +3 -0
  46. package/lib/payment/progress-item.d.ts +12 -0
  47. package/lib/payment/progress-item.js +107 -0
  48. package/lib/payment/success.d.ts +4 -1
  49. package/lib/payment/success.js +59 -3
  50. package/package.json +3 -3
  51. package/src/locales/en.tsx +7 -0
  52. package/src/locales/zh.tsx +8 -1
  53. package/src/payment/index.tsx +6 -0
  54. package/src/payment/progress-item.tsx +107 -0
  55. package/src/payment/success.tsx +88 -3
@@ -182,6 +182,13 @@ export default flat({
182
182
  donate: 'Thanks for your tip',
183
183
  tip: 'A payment to {payee} has been completed. You can view the details of this payment in your account.',
184
184
  },
185
+ vendor: {
186
+ processing: 'Processing...',
187
+ progress: 'Progress {progress}%',
188
+ delivered: 'Installation completed',
189
+ failed: 'Processing failed',
190
+ pending: 'Waiting for processing',
191
+ },
185
192
  confirm: {
186
193
  withStake:
187
194
  'By confirming, you allow {payee} to charge your account for future payments and, if necessary, slash your stake. You can cancel your subscription or withdraw your stake at any time.',
@@ -179,7 +179,14 @@ export default flat({
179
179
  subscription: '感谢您的订阅',
180
180
  setup: '感谢您的订阅',
181
181
  donate: '感谢您的支持',
182
- tip: '向{payee}的付款已完成。您可以在您的账户中查看此付款的详细信息。',
182
+ tip: '向 {payee} 的付款已完成。您可以在您的账户中查看此付款的详细信息。',
183
+ },
184
+ vendor: {
185
+ processing: '正在安装中...',
186
+ progress: '进度 {progress}%',
187
+ delivered: '安装成功',
188
+ failed: '安装失败',
189
+ pending: '准备安装',
183
190
  },
184
191
  confirm: {
185
192
  withStake:
@@ -307,6 +307,12 @@ function PaymentInner({
307
307
  {completed && (
308
308
  <PaymentSuccess
309
309
  mode={mode}
310
+ pageInfo={state.checkoutSession.metadata?.page_info}
311
+ hasVendor={
312
+ state.checkoutSession.metadata?.page_info?.hasVendor ||
313
+ state.checkoutSession.line_items.some((item: any) => item.price?.product?.vendor_config?.length > 0)
314
+ }
315
+ sessionId={state.checkoutSession.id}
310
316
  payee={getStatementDescriptor(state.checkoutSession.line_items)}
311
317
  action={state.checkoutSession.mode}
312
318
  invoiceId={state.checkoutSession.invoice_id}
@@ -0,0 +1,107 @@
1
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
+ import { Box, LinearProgress, Typography } from '@mui/material';
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+
5
+ interface VendorStatus {
6
+ success: boolean;
7
+ status: 'delivered' | 'pending' | 'failed';
8
+ progress: number;
9
+ message: string;
10
+ appUrl?: string;
11
+ title?: string;
12
+ }
13
+
14
+ export function VendorProgressItem({ vendor }: { vendor: VendorStatus }) {
15
+ const { t } = useLocaleContext();
16
+ const [displayProgress, setDisplayProgress] = useState(0);
17
+ const animationRef = useRef<number>();
18
+
19
+ const startAnimation = useCallback(() => {
20
+ const realProgress = vendor.progress || 0;
21
+ let startTime: number;
22
+ let startProgress: number;
23
+
24
+ const animate = (currentTime: number) => {
25
+ if (!startTime) {
26
+ startTime = currentTime;
27
+ startProgress = displayProgress;
28
+ }
29
+
30
+ const elapsed = currentTime - startTime;
31
+ let newProgress: number;
32
+
33
+ if (realProgress === 100) {
34
+ // If progress is 100%, set directly to 100% without animation
35
+ newProgress = 100;
36
+ } else if (realProgress === 0) {
37
+ // When no data available, increase 1% per second
38
+ newProgress = Math.min(startProgress + elapsed / 1000, 99);
39
+ } else if (realProgress > startProgress) {
40
+ const duration = 1000; // 1 second to reach target quickly
41
+ const progress = Math.min(elapsed / duration, 1);
42
+ newProgress = startProgress + (realProgress - startProgress) * progress;
43
+ } else {
44
+ // After reaching target, increase 1% per second, max 99%
45
+ newProgress = Math.min(startProgress + elapsed / 1000, 99);
46
+ }
47
+
48
+ // Ensure progress is an integer
49
+ newProgress = Math.round(newProgress);
50
+ setDisplayProgress((pre) => (pre > newProgress ? pre : newProgress));
51
+
52
+ // Stop animation immediately when 100%
53
+ if (realProgress === 100) {
54
+ return;
55
+ }
56
+
57
+ // Continue animation in other cases
58
+ if (newProgress < 99 && realProgress < 100) {
59
+ animationRef.current = requestAnimationFrame(animate);
60
+ }
61
+ };
62
+
63
+ if (animationRef.current) {
64
+ cancelAnimationFrame(animationRef.current);
65
+ }
66
+ animationRef.current = requestAnimationFrame(animate);
67
+ }, [vendor.progress, displayProgress]);
68
+
69
+ useEffect(() => {
70
+ startAnimation();
71
+ return () => {
72
+ if (animationRef.current) {
73
+ cancelAnimationFrame(animationRef.current);
74
+ }
75
+ };
76
+ }, [startAnimation]);
77
+
78
+ const isCompleted = displayProgress >= 80;
79
+ const statusText = isCompleted ? t('payment.checkout.vendor.delivered') : t('payment.checkout.vendor.processing');
80
+
81
+ return (
82
+ <Box sx={{ mb: 2 }}>
83
+ <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1 }}>
84
+ <Typography variant="body2" sx={{ color: 'text.secondary' }}>
85
+ {vendor.title ? `${vendor.title} - ${statusText}` : statusText}
86
+ </Typography>
87
+ <Typography variant="body2" sx={{ color: 'text.secondary', fontWeight: 500 }}>
88
+ {t('payment.checkout.vendor.progress', { progress: displayProgress })}
89
+ </Typography>
90
+ </Box>
91
+ <LinearProgress
92
+ variant="determinate"
93
+ value={Math.min(displayProgress, 100)}
94
+ sx={{
95
+ height: 8,
96
+ borderRadius: 4,
97
+ backgroundColor: 'grey.200',
98
+ '& .MuiLinearProgress-bar': {
99
+ borderRadius: 4,
100
+ backgroundColor: isCompleted ? 'success.main' : 'primary.main',
101
+ transition: 'background-color 0.3s linear',
102
+ },
103
+ }}
104
+ />
105
+ </Box>
106
+ );
107
+ }
@@ -1,12 +1,17 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
- import { Grow, Link, Stack, Typography, Box, Paper, styled } from '@mui/material';
2
+ import { Box, Grow, Link, Paper, Stack, styled, Typography } from '@mui/material';
3
+ import { useEffect, useState } from 'react';
3
4
  import { joinURL } from 'ufo';
4
5
 
5
6
  import { Button } from '@arcblock/ux';
6
7
  import { usePaymentContext } from '../contexts/payment';
8
+ import { VendorProgressItem } from './progress-item';
7
9
 
8
10
  type Props = {
9
11
  mode: string;
12
+ pageInfo?: any;
13
+ hasVendor?: boolean;
14
+ sessionId?: string;
10
15
  message: string;
11
16
  action: string;
12
17
  payee: string;
@@ -15,8 +20,27 @@ type Props = {
15
20
  subscriptions?: any[];
16
21
  };
17
22
 
23
+ interface VendorStatus {
24
+ success: boolean;
25
+ status: 'delivered' | 'pending' | 'failed';
26
+ progress: number;
27
+ message: string;
28
+ appUrl?: string;
29
+ title?: string;
30
+ }
31
+
32
+ interface VendorResponse {
33
+ payment_status: string;
34
+ session_status: string;
35
+ vendors: VendorStatus[];
36
+ error: string | null;
37
+ }
38
+
18
39
  export default function PaymentSuccess({
19
40
  mode,
41
+ pageInfo = {},
42
+ hasVendor = false,
43
+ sessionId = '',
20
44
  message,
21
45
  action,
22
46
  payee,
@@ -24,9 +48,69 @@ export default function PaymentSuccess({
24
48
  subscriptionId = '',
25
49
  subscriptions = [],
26
50
  }: Props) {
27
- const { t } = useLocaleContext();
28
- const { prefix } = usePaymentContext();
51
+ const { t, locale } = useLocaleContext();
52
+ const { prefix, api } = usePaymentContext();
53
+ const [vendorStatus, setVendorStatus] = useState<VendorResponse | null>(null);
54
+ const [isAllCompleted, setIsAllCompleted] = useState(false);
29
55
  let next: any = null;
56
+
57
+ // Fetch vendor status when hasVendor is true
58
+ useEffect(() => {
59
+ if (!hasVendor || !sessionId) return undefined;
60
+
61
+ const fetchVendorStatus = async (interval?: NodeJS.Timeout) => {
62
+ try {
63
+ const response = await api.get(joinURL(prefix, `/api/vendors/order/${sessionId}/status`), {});
64
+ const allCompleted = response.data?.vendors?.every((vendor: any) => vendor.progress >= 80);
65
+ if (allCompleted) {
66
+ clearInterval(interval);
67
+ }
68
+ setIsAllCompleted(allCompleted);
69
+ setVendorStatus(response.data);
70
+ } catch (error) {
71
+ console.error('Failed to fetch vendor status:', error);
72
+ }
73
+ };
74
+
75
+ fetchVendorStatus();
76
+
77
+ // Poll every 5 seconds if progress is less than 80%
78
+ const interval = setInterval(() => {
79
+ fetchVendorStatus(interval);
80
+ }, 5000);
81
+
82
+ return () => clearInterval(interval);
83
+ }, [hasVendor, api, prefix, sessionId]);
84
+
85
+ // Render vendor progress component
86
+ const renderVendorProgress = () => {
87
+ if (!hasVendor || !vendorStatus) return null;
88
+
89
+ return (
90
+ <Paper
91
+ elevation={0}
92
+ sx={{
93
+ p: 3,
94
+ backgroundColor: 'grey.50',
95
+ borderRadius: 2,
96
+ width: '100%',
97
+ mt: 2,
98
+ display: 'flex',
99
+ flexDirection: 'column',
100
+ gap: 2,
101
+ }}>
102
+ {vendorStatus.vendors?.map((vendor, index) => (
103
+ <VendorProgressItem key={vendor.title || `vendor-${index}`} vendor={vendor} />
104
+ ))}
105
+ {pageInfo?.success_message?.[locale] && isAllCompleted ? (
106
+ <Typography variant="h6" sx={{ color: 'text.primary', mb: 1 }}>
107
+ {pageInfo?.success_message?.[locale]}
108
+ </Typography>
109
+ ) : null}
110
+ </Paper>
111
+ );
112
+ };
113
+
30
114
  if (['subscription', 'setup'].includes(action)) {
31
115
  if (subscriptions && subscriptions.length > 1) {
32
116
  next = (
@@ -134,6 +218,7 @@ export default function PaymentSuccess({
134
218
  }}>
135
219
  {t('payment.checkout.completed.tip', { payee })}
136
220
  </Typography>
221
+ {renderVendorProgress()}
137
222
  {next}
138
223
  </Stack>
139
224
  </Grow>