@blocklet/payment-react 1.20.4 → 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 +7 -7
  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
package/docs/providers.md CHANGED
@@ -1,101 +1,42 @@
1
1
  # Providers
2
2
 
3
- Providers are the foundation of the `@blocklet/payment-react` library. They use React's Context API to manage state and share essential data—like user sessions, payment settings, and API clients—across all payment-related components. Wrapping your application in the appropriate provider is a necessary first step before using any of the library's components.
3
+ Context Providers are a core concept in `@blocklet/payment-react`. They act as wrappers around parts of your application, using React's Context API to pass down essential data and functions to all the components nested within them. This avoids the need to pass props down manually through many levels of your component tree.
4
4
 
5
- This library offers two primary providers that serve distinct purposes:
5
+ To use most of the components in this library, you must wrap your application or relevant pages with the appropriate provider.
6
6
 
7
- ```d2
7
+ ```d2 Basic Provider Structure icon=graph:flowchart
8
8
  direction: down
9
9
 
10
- "Your App" {
11
- "PaymentProvider": {
12
- style.fill: "#DDF0FF"
13
- "CheckoutForm"
14
- "CustomerInvoiceList"
10
+ Your-Application: {
11
+ shape: rectangle
15
12
 
16
- "DonateProvider": {
17
- style.fill: "#E3F9E5"
18
- "CheckoutDonate"
13
+ PaymentProvider: {
14
+ label: "PaymentProvider"
15
+ shape: rectangle
16
+
17
+ CheckoutForm: {
18
+ label: "CheckoutForm"
19
+ }
20
+ CustomerInvoiceList: {
21
+ label: "CustomerInvoiceList"
19
22
  }
20
23
  }
21
24
  }
22
25
 
23
- "Your App" -> "PaymentProvider": "Wraps the application"
24
- "PaymentProvider" -> "CheckoutForm": "Provides context"
25
- "PaymentProvider" -> "CustomerInvoiceList": "Provides context"
26
- "PaymentProvider" -> "DonateProvider": "Can be nested inside"
27
- "DonateProvider" -> "CheckoutDonate": "Provides context for donations"
28
- ```
29
-
30
- ## PaymentProvider
31
-
32
- The `PaymentProvider` is the main provider that you will use in almost every integration. It is responsible for handling user authentication, fetching payment method settings, managing test/live modes, and providing a pre-configured API client to all nested components. Any component that deals with payments, subscriptions, or invoices must be a descendant of `PaymentProvider`.
33
-
34
- It centralizes access to critical data, ensuring a consistent and secure payment environment.
35
-
36
- [Learn more about PaymentProvider ›](./providers-payment-provider.md)
37
-
38
- ## DonateProvider
39
-
40
- The `DonateProvider` is designed specifically for donation features. It manages the configuration and state for the `CheckoutDonate` component, such as preset amounts, button text, and donation history display.
41
-
42
- It is important to note that the `DonateProvider` must be used *within* a `PaymentProvider`, as it relies on the core payment context to process donations.
43
-
44
- [Learn more about DonateProvider ›](./providers-donate-provider.md)
45
-
46
- ### Basic Setup Example
47
-
48
- Here is a typical setup showing how to nest the providers. The `useSessionContext` hook is a placeholder for your application's own session management logic, which supplies the `session` and `connectApi` props required by `PaymentProvider`.
49
-
50
- ```tsx
51
- import {
52
- PaymentProvider,
53
- DonateProvider,
54
- CheckoutForm,
55
- CheckoutDonate,
56
- } from '@blocklet/payment-react';
57
- // This hook should be implemented in your application to provide session context.
58
- // See the PaymentProvider documentation for an example implementation.
59
- import { useSessionContext } from '../path/to/your/session-context';
60
-
61
- function App() {
62
- const { session, connectApi } = useSessionContext();
63
-
64
- // Render only when the session is available.
65
- if (!session) {
66
- return <div>Loading Session...</div>;
67
- }
68
-
69
- return (
70
- <PaymentProvider session={session} connect={connectApi}>
71
- {/* General payment components can be placed directly under PaymentProvider */}
72
- <CheckoutForm id="plink_xxx" />
73
-
74
- {/* For donation features, wrap the relevant part with DonateProvider */}
75
- <DonateProvider
76
- mountLocation="your-unique-instance-id"
77
- description="Donations for my awesome project"
78
- >
79
- <CheckoutDonate
80
- settings={{
81
- target: 'project-donate',
82
- title: 'Support This Project',
83
- description: 'Your contribution is appreciated!',
84
- reference: 'https://your-site.com/project',
85
- beneficiaries: [{
86
- address: 'user-did-address',
87
- share: '100'
88
- }]
89
- }}
90
- />
91
- </DonateProvider>
92
- </PaymentProvider>
93
- );
94
- }
26
+ Your-Application -> PaymentProvider: "Wraps"
27
+ PaymentProvider -> CheckoutForm: "Provides context"
28
+ PaymentProvider -> CustomerInvoiceList: "Provides context"
95
29
  ```
96
30
 
97
- ---
31
+ The library offers two main providers, each serving a distinct purpose. Choose the one that fits your needs.
98
32
 
99
- With an understanding of the providers, you are ready to explore their specific configurations. We recommend starting with the `PaymentProvider` as it is fundamental to the library's operation.
33
+ <x-cards>
34
+ <x-card data-title="PaymentProvider" data-icon="lucide:package" data-href="/providers/payment-provider">
35
+ The fundamental provider required for nearly all payment-related operations. It manages payment methods, currency settings, user session information, and provides a pre-configured API client.
36
+ </x-card>
37
+ <x-card data-title="DonateProvider" data-icon="lucide:heart" data-href="/providers/donate-provider">
38
+ A specialized provider for donation features. It handles the configuration and state for donation widgets, such as those implemented with the CheckoutDonate component.
39
+ </x-card>
40
+ </x-cards>
100
41
 
101
- **Next:** [PaymentProvider](./providers-payment-provider.md)
42
+ Click on a provider above to view its detailed documentation, including required props and setup examples.
package/es/locales/en.js CHANGED
@@ -179,6 +179,13 @@ export default flat({
179
179
  donate: "Thanks for your tip",
180
180
  tip: "A payment to {payee} has been completed. You can view the details of this payment in your account."
181
181
  },
182
+ vendor: {
183
+ processing: "Processing...",
184
+ progress: "Progress {progress}%",
185
+ delivered: "Installation completed",
186
+ failed: "Processing failed",
187
+ pending: "Waiting for processing"
188
+ },
182
189
  confirm: {
183
190
  withStake: "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.",
184
191
  withoutStake: "By confirming, you allow {payee} to charge your account for future payments. You can cancel your subscription at any time."
package/es/locales/zh.js CHANGED
@@ -177,7 +177,14 @@ export default flat({
177
177
  subscription: "\u611F\u8C22\u60A8\u7684\u8BA2\u9605",
178
178
  setup: "\u611F\u8C22\u60A8\u7684\u8BA2\u9605",
179
179
  donate: "\u611F\u8C22\u60A8\u7684\u652F\u6301",
180
- tip: "\u5411{payee}\u7684\u4ED8\u6B3E\u5DF2\u5B8C\u6210\u3002\u60A8\u53EF\u4EE5\u5728\u60A8\u7684\u8D26\u6237\u4E2D\u67E5\u770B\u6B64\u4ED8\u6B3E\u7684\u8BE6\u7EC6\u4FE1\u606F\u3002"
180
+ tip: "\u5411 {payee} \u7684\u4ED8\u6B3E\u5DF2\u5B8C\u6210\u3002\u60A8\u53EF\u4EE5\u5728\u60A8\u7684\u8D26\u6237\u4E2D\u67E5\u770B\u6B64\u4ED8\u6B3E\u7684\u8BE6\u7EC6\u4FE1\u606F\u3002"
181
+ },
182
+ vendor: {
183
+ processing: "\u6B63\u5728\u5B89\u88C5\u4E2D...",
184
+ progress: "\u8FDB\u5EA6 {progress}%",
185
+ delivered: "\u5B89\u88C5\u6210\u529F",
186
+ failed: "\u5B89\u88C5\u5931\u8D25",
187
+ pending: "\u51C6\u5907\u5B89\u88C5"
181
188
  },
182
189
  confirm: {
183
190
  withStake: "\u786E\u8BA4\u8BA2\u9605\uFF0C\u5373\u8868\u793A\u60A8\u6388\u6743 {payee} \u4ECE\u60A8\u7684\u8D26\u6237\u6263\u53D6\u672A\u6765\u6B3E\u9879\uFF0C\u5E76\u5728\u5FC5\u8981\u65F6\u7F5A\u6CA1\u8D28\u62BC\u3002\u60A8\u53EF\u968F\u65F6\u53D6\u6D88\u8BA2\u9605\u6216\u64A4\u9500\u8D28\u62BC\u3002",
@@ -255,6 +255,9 @@ function PaymentInner({
255
255
  PaymentSuccess,
256
256
  {
257
257
  mode,
258
+ pageInfo: state.checkoutSession.metadata?.page_info,
259
+ hasVendor: state.checkoutSession.metadata?.page_info?.hasVendor || state.checkoutSession.line_items.some((item) => item.price?.product?.vendor_config?.length > 0),
260
+ sessionId: state.checkoutSession.id,
258
261
  payee: getStatementDescriptor(state.checkoutSession.line_items),
259
262
  action: state.checkoutSession.mode,
260
263
  invoiceId: state.checkoutSession.invoice_id,
@@ -0,0 +1,12 @@
1
+ interface VendorStatus {
2
+ success: boolean;
3
+ status: 'delivered' | 'pending' | 'failed';
4
+ progress: number;
5
+ message: string;
6
+ appUrl?: string;
7
+ title?: string;
8
+ }
9
+ export declare function VendorProgressItem({ vendor }: {
10
+ vendor: VendorStatus;
11
+ }): import("react").JSX.Element;
12
+ export {};
@@ -0,0 +1,78 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
3
+ import { Box, LinearProgress, Typography } from "@mui/material";
4
+ import { useCallback, useEffect, useRef, useState } from "react";
5
+ export function VendorProgressItem({ vendor }) {
6
+ const { t } = useLocaleContext();
7
+ const [displayProgress, setDisplayProgress] = useState(0);
8
+ const animationRef = useRef();
9
+ const startAnimation = useCallback(() => {
10
+ const realProgress = vendor.progress || 0;
11
+ let startTime;
12
+ let startProgress;
13
+ const animate = (currentTime) => {
14
+ if (!startTime) {
15
+ startTime = currentTime;
16
+ startProgress = displayProgress;
17
+ }
18
+ const elapsed = currentTime - startTime;
19
+ let newProgress;
20
+ if (realProgress === 100) {
21
+ newProgress = 100;
22
+ } else if (realProgress === 0) {
23
+ newProgress = Math.min(startProgress + elapsed / 1e3, 99);
24
+ } else if (realProgress > startProgress) {
25
+ const duration = 1e3;
26
+ const progress = Math.min(elapsed / duration, 1);
27
+ newProgress = startProgress + (realProgress - startProgress) * progress;
28
+ } else {
29
+ newProgress = Math.min(startProgress + elapsed / 1e3, 99);
30
+ }
31
+ newProgress = Math.round(newProgress);
32
+ setDisplayProgress((pre) => pre > newProgress ? pre : newProgress);
33
+ if (realProgress === 100) {
34
+ return;
35
+ }
36
+ if (newProgress < 99 && realProgress < 100) {
37
+ animationRef.current = requestAnimationFrame(animate);
38
+ }
39
+ };
40
+ if (animationRef.current) {
41
+ cancelAnimationFrame(animationRef.current);
42
+ }
43
+ animationRef.current = requestAnimationFrame(animate);
44
+ }, [vendor.progress, displayProgress]);
45
+ useEffect(() => {
46
+ startAnimation();
47
+ return () => {
48
+ if (animationRef.current) {
49
+ cancelAnimationFrame(animationRef.current);
50
+ }
51
+ };
52
+ }, [startAnimation]);
53
+ const isCompleted = displayProgress >= 80;
54
+ const statusText = isCompleted ? t("payment.checkout.vendor.delivered") : t("payment.checkout.vendor.processing");
55
+ return /* @__PURE__ */ jsxs(Box, { sx: { mb: 2 }, children: [
56
+ /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1 }, children: [
57
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: "text.secondary" }, children: vendor.title ? `${vendor.title} - ${statusText}` : statusText }),
58
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: "text.secondary", fontWeight: 500 }, children: t("payment.checkout.vendor.progress", { progress: displayProgress }) })
59
+ ] }),
60
+ /* @__PURE__ */ jsx(
61
+ LinearProgress,
62
+ {
63
+ variant: "determinate",
64
+ value: Math.min(displayProgress, 100),
65
+ sx: {
66
+ height: 8,
67
+ borderRadius: 4,
68
+ backgroundColor: "grey.200",
69
+ "& .MuiLinearProgress-bar": {
70
+ borderRadius: 4,
71
+ backgroundColor: isCompleted ? "success.main" : "primary.main",
72
+ transition: "background-color 0.3s linear"
73
+ }
74
+ }
75
+ }
76
+ )
77
+ ] });
78
+ }
@@ -1,5 +1,8 @@
1
1
  type Props = {
2
2
  mode: string;
3
+ pageInfo?: any;
4
+ hasVendor?: boolean;
5
+ sessionId?: string;
3
6
  message: string;
4
7
  action: string;
5
8
  payee: string;
@@ -7,5 +10,5 @@ type Props = {
7
10
  subscriptionId?: string;
8
11
  subscriptions?: any[];
9
12
  };
10
- export default function PaymentSuccess({ mode, message, action, payee, invoiceId, subscriptionId, subscriptions, }: Props): import("react").JSX.Element;
13
+ export default function PaymentSuccess({ mode, pageInfo, hasVendor, sessionId, message, action, payee, invoiceId, subscriptionId, subscriptions, }: Props): import("react").JSX.Element;
11
14
  export {};
@@ -1,11 +1,16 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
3
- import { Grow, Link, Stack, Typography, Box, Paper, styled } from "@mui/material";
3
+ import { Box, Grow, Link, Paper, Stack, styled, Typography } from "@mui/material";
4
+ import { useEffect, useState } from "react";
4
5
  import { joinURL } from "ufo";
5
6
  import { Button } from "@arcblock/ux";
6
7
  import { usePaymentContext } from "../contexts/payment.js";
8
+ import { VendorProgressItem } from "./progress-item.js";
7
9
  export default function PaymentSuccess({
8
10
  mode,
11
+ pageInfo = {},
12
+ hasVendor = false,
13
+ sessionId = "",
9
14
  message,
10
15
  action,
11
16
  payee,
@@ -13,9 +18,55 @@ export default function PaymentSuccess({
13
18
  subscriptionId = "",
14
19
  subscriptions = []
15
20
  }) {
16
- const { t } = useLocaleContext();
17
- const { prefix } = usePaymentContext();
21
+ const { t, locale } = useLocaleContext();
22
+ const { prefix, api } = usePaymentContext();
23
+ const [vendorStatus, setVendorStatus] = useState(null);
24
+ const [isAllCompleted, setIsAllCompleted] = useState(false);
18
25
  let next = null;
26
+ useEffect(() => {
27
+ if (!hasVendor || !sessionId) return void 0;
28
+ const fetchVendorStatus = async (interval2) => {
29
+ try {
30
+ const response = await api.get(joinURL(prefix, `/api/vendors/order/${sessionId}/status`), {});
31
+ const allCompleted = response.data?.vendors?.every((vendor) => vendor.progress >= 80);
32
+ if (allCompleted) {
33
+ clearInterval(interval2);
34
+ }
35
+ setIsAllCompleted(allCompleted);
36
+ setVendorStatus(response.data);
37
+ } catch (error) {
38
+ console.error("Failed to fetch vendor status:", error);
39
+ }
40
+ };
41
+ fetchVendorStatus();
42
+ const interval = setInterval(() => {
43
+ fetchVendorStatus(interval);
44
+ }, 5e3);
45
+ return () => clearInterval(interval);
46
+ }, [hasVendor, api, prefix, sessionId]);
47
+ const renderVendorProgress = () => {
48
+ if (!hasVendor || !vendorStatus) return null;
49
+ return /* @__PURE__ */ jsxs(
50
+ Paper,
51
+ {
52
+ elevation: 0,
53
+ sx: {
54
+ p: 3,
55
+ backgroundColor: "grey.50",
56
+ borderRadius: 2,
57
+ width: "100%",
58
+ mt: 2,
59
+ display: "flex",
60
+ flexDirection: "column",
61
+ gap: 2
62
+ },
63
+ children: [
64
+ vendorStatus.vendors?.map((vendor, index) => /* @__PURE__ */ jsx(VendorProgressItem, { vendor }, vendor.title || `vendor-${index}`)),
65
+ pageInfo?.success_message?.[locale] && isAllCompleted ? /* @__PURE__ */ jsx(Typography, { variant: "h6", sx: { color: "text.primary", mb: 1 }, children: pageInfo?.success_message?.[locale] }) : null
66
+ ]
67
+ }
68
+ );
69
+ };
19
70
  if (["subscription", "setup"].includes(action)) {
20
71
  if (subscriptions && subscriptions.length > 1) {
21
72
  next = /* @__PURE__ */ jsx(
@@ -131,6 +182,7 @@ export default function PaymentSuccess({
131
182
  children: t("payment.checkout.completed.tip", { payee })
132
183
  }
133
184
  ),
185
+ renderVendorProgress(),
134
186
  next
135
187
  ]
136
188
  }
package/lib/locales/en.js CHANGED
@@ -186,6 +186,13 @@ module.exports = (0, _flat.default)({
186
186
  donate: "Thanks for your tip",
187
187
  tip: "A payment to {payee} has been completed. You can view the details of this payment in your account."
188
188
  },
189
+ vendor: {
190
+ processing: "Processing...",
191
+ progress: "Progress {progress}%",
192
+ delivered: "Installation completed",
193
+ failed: "Processing failed",
194
+ pending: "Waiting for processing"
195
+ },
189
196
  confirm: {
190
197
  withStake: "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.",
191
198
  withoutStake: "By confirming, you allow {payee} to charge your account for future payments. You can cancel your subscription at any time."
package/lib/locales/zh.js CHANGED
@@ -184,7 +184,14 @@ module.exports = (0, _flat.default)({
184
184
  subscription: "\u611F\u8C22\u60A8\u7684\u8BA2\u9605",
185
185
  setup: "\u611F\u8C22\u60A8\u7684\u8BA2\u9605",
186
186
  donate: "\u611F\u8C22\u60A8\u7684\u652F\u6301",
187
- tip: "\u5411{payee}\u7684\u4ED8\u6B3E\u5DF2\u5B8C\u6210\u3002\u60A8\u53EF\u4EE5\u5728\u60A8\u7684\u8D26\u6237\u4E2D\u67E5\u770B\u6B64\u4ED8\u6B3E\u7684\u8BE6\u7EC6\u4FE1\u606F\u3002"
187
+ tip: "\u5411 {payee} \u7684\u4ED8\u6B3E\u5DF2\u5B8C\u6210\u3002\u60A8\u53EF\u4EE5\u5728\u60A8\u7684\u8D26\u6237\u4E2D\u67E5\u770B\u6B64\u4ED8\u6B3E\u7684\u8BE6\u7EC6\u4FE1\u606F\u3002"
188
+ },
189
+ vendor: {
190
+ processing: "\u6B63\u5728\u5B89\u88C5\u4E2D...",
191
+ progress: "\u8FDB\u5EA6 {progress}%",
192
+ delivered: "\u5B89\u88C5\u6210\u529F",
193
+ failed: "\u5B89\u88C5\u5931\u8D25",
194
+ pending: "\u51C6\u5907\u5B89\u88C5"
188
195
  },
189
196
  confirm: {
190
197
  withStake: "\u786E\u8BA4\u8BA2\u9605\uFF0C\u5373\u8868\u793A\u60A8\u6388\u6743 {payee} \u4ECE\u60A8\u7684\u8D26\u6237\u6263\u53D6\u672A\u6765\u6B3E\u9879\uFF0C\u5E76\u5728\u5FC5\u8981\u65F6\u7F5A\u6CA1\u8D28\u62BC\u3002\u60A8\u53EF\u968F\u65F6\u53D6\u6D88\u8BA2\u9605\u6216\u64A4\u9500\u8D28\u62BC\u3002",
@@ -307,6 +307,9 @@ function PaymentInner({
307
307
  },
308
308
  children: [completed && /* @__PURE__ */(0, _jsxRuntime.jsx)(_success.default, {
309
309
  mode,
310
+ pageInfo: state.checkoutSession.metadata?.page_info,
311
+ hasVendor: state.checkoutSession.metadata?.page_info?.hasVendor || state.checkoutSession.line_items.some(item => item.price?.product?.vendor_config?.length > 0),
312
+ sessionId: state.checkoutSession.id,
310
313
  payee: (0, _util2.getStatementDescriptor)(state.checkoutSession.line_items),
311
314
  action: state.checkoutSession.mode,
312
315
  invoiceId: state.checkoutSession.invoice_id,
@@ -0,0 +1,12 @@
1
+ interface VendorStatus {
2
+ success: boolean;
3
+ status: 'delivered' | 'pending' | 'failed';
4
+ progress: number;
5
+ message: string;
6
+ appUrl?: string;
7
+ title?: string;
8
+ }
9
+ export declare function VendorProgressItem({ vendor }: {
10
+ vendor: VendorStatus;
11
+ }): import("react").JSX.Element;
12
+ export {};
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.VendorProgressItem = VendorProgressItem;
7
+ var _jsxRuntime = require("react/jsx-runtime");
8
+ var _context = require("@arcblock/ux/lib/Locale/context");
9
+ var _material = require("@mui/material");
10
+ var _react = require("react");
11
+ function VendorProgressItem({
12
+ vendor
13
+ }) {
14
+ const {
15
+ t
16
+ } = (0, _context.useLocaleContext)();
17
+ const [displayProgress, setDisplayProgress] = (0, _react.useState)(0);
18
+ const animationRef = (0, _react.useRef)();
19
+ const startAnimation = (0, _react.useCallback)(() => {
20
+ const realProgress = vendor.progress || 0;
21
+ let startTime;
22
+ let startProgress;
23
+ const animate = currentTime => {
24
+ if (!startTime) {
25
+ startTime = currentTime;
26
+ startProgress = displayProgress;
27
+ }
28
+ const elapsed = currentTime - startTime;
29
+ let newProgress;
30
+ if (realProgress === 100) {
31
+ newProgress = 100;
32
+ } else if (realProgress === 0) {
33
+ newProgress = Math.min(startProgress + elapsed / 1e3, 99);
34
+ } else if (realProgress > startProgress) {
35
+ const duration = 1e3;
36
+ const progress = Math.min(elapsed / duration, 1);
37
+ newProgress = startProgress + (realProgress - startProgress) * progress;
38
+ } else {
39
+ newProgress = Math.min(startProgress + elapsed / 1e3, 99);
40
+ }
41
+ newProgress = Math.round(newProgress);
42
+ setDisplayProgress(pre => pre > newProgress ? pre : newProgress);
43
+ if (realProgress === 100) {
44
+ return;
45
+ }
46
+ if (newProgress < 99 && realProgress < 100) {
47
+ animationRef.current = requestAnimationFrame(animate);
48
+ }
49
+ };
50
+ if (animationRef.current) {
51
+ cancelAnimationFrame(animationRef.current);
52
+ }
53
+ animationRef.current = requestAnimationFrame(animate);
54
+ }, [vendor.progress, displayProgress]);
55
+ (0, _react.useEffect)(() => {
56
+ startAnimation();
57
+ return () => {
58
+ if (animationRef.current) {
59
+ cancelAnimationFrame(animationRef.current);
60
+ }
61
+ };
62
+ }, [startAnimation]);
63
+ const isCompleted = displayProgress >= 80;
64
+ const statusText = isCompleted ? t("payment.checkout.vendor.delivered") : t("payment.checkout.vendor.processing");
65
+ return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
66
+ sx: {
67
+ mb: 2
68
+ },
69
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
70
+ sx: {
71
+ display: "flex",
72
+ justifyContent: "space-between",
73
+ alignItems: "center",
74
+ mb: 1
75
+ },
76
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
77
+ variant: "body2",
78
+ sx: {
79
+ color: "text.secondary"
80
+ },
81
+ children: vendor.title ? `${vendor.title} - ${statusText}` : statusText
82
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
83
+ variant: "body2",
84
+ sx: {
85
+ color: "text.secondary",
86
+ fontWeight: 500
87
+ },
88
+ children: t("payment.checkout.vendor.progress", {
89
+ progress: displayProgress
90
+ })
91
+ })]
92
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.LinearProgress, {
93
+ variant: "determinate",
94
+ value: Math.min(displayProgress, 100),
95
+ sx: {
96
+ height: 8,
97
+ borderRadius: 4,
98
+ backgroundColor: "grey.200",
99
+ "& .MuiLinearProgress-bar": {
100
+ borderRadius: 4,
101
+ backgroundColor: isCompleted ? "success.main" : "primary.main",
102
+ transition: "background-color 0.3s linear"
103
+ }
104
+ }
105
+ })]
106
+ });
107
+ }
@@ -1,5 +1,8 @@
1
1
  type Props = {
2
2
  mode: string;
3
+ pageInfo?: any;
4
+ hasVendor?: boolean;
5
+ sessionId?: string;
3
6
  message: string;
4
7
  action: string;
5
8
  payee: string;
@@ -7,5 +10,5 @@ type Props = {
7
10
  subscriptionId?: string;
8
11
  subscriptions?: any[];
9
12
  };
10
- export default function PaymentSuccess({ mode, message, action, payee, invoiceId, subscriptionId, subscriptions, }: Props): import("react").JSX.Element;
13
+ export default function PaymentSuccess({ mode, pageInfo, hasVendor, sessionId, message, action, payee, invoiceId, subscriptionId, subscriptions, }: Props): import("react").JSX.Element;
11
14
  export {};
@@ -7,11 +7,16 @@ module.exports = PaymentSuccess;
7
7
  var _jsxRuntime = require("react/jsx-runtime");
8
8
  var _context = require("@arcblock/ux/lib/Locale/context");
9
9
  var _material = require("@mui/material");
10
+ var _react = require("react");
10
11
  var _ufo = require("ufo");
11
12
  var _ux = require("@arcblock/ux");
12
13
  var _payment = require("../contexts/payment");
14
+ var _progressItem = require("./progress-item");
13
15
  function PaymentSuccess({
14
16
  mode,
17
+ pageInfo = {},
18
+ hasVendor = false,
19
+ sessionId = "",
15
20
  message,
16
21
  action,
17
22
  payee,
@@ -20,12 +25,63 @@ function PaymentSuccess({
20
25
  subscriptions = []
21
26
  }) {
22
27
  const {
23
- t
28
+ t,
29
+ locale
24
30
  } = (0, _context.useLocaleContext)();
25
31
  const {
26
- prefix
32
+ prefix,
33
+ api
27
34
  } = (0, _payment.usePaymentContext)();
35
+ const [vendorStatus, setVendorStatus] = (0, _react.useState)(null);
36
+ const [isAllCompleted, setIsAllCompleted] = (0, _react.useState)(false);
28
37
  let next = null;
38
+ (0, _react.useEffect)(() => {
39
+ if (!hasVendor || !sessionId) return void 0;
40
+ const fetchVendorStatus = async interval2 => {
41
+ try {
42
+ const response = await api.get((0, _ufo.joinURL)(prefix, `/api/vendors/order/${sessionId}/status`), {});
43
+ const allCompleted = response.data?.vendors?.every(vendor => vendor.progress >= 80);
44
+ if (allCompleted) {
45
+ clearInterval(interval2);
46
+ }
47
+ setIsAllCompleted(allCompleted);
48
+ setVendorStatus(response.data);
49
+ } catch (error) {
50
+ console.error("Failed to fetch vendor status:", error);
51
+ }
52
+ };
53
+ fetchVendorStatus();
54
+ const interval = setInterval(() => {
55
+ fetchVendorStatus(interval);
56
+ }, 5e3);
57
+ return () => clearInterval(interval);
58
+ }, [hasVendor, api, prefix, sessionId]);
59
+ const renderVendorProgress = () => {
60
+ if (!hasVendor || !vendorStatus) return null;
61
+ return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Paper, {
62
+ elevation: 0,
63
+ sx: {
64
+ p: 3,
65
+ backgroundColor: "grey.50",
66
+ borderRadius: 2,
67
+ width: "100%",
68
+ mt: 2,
69
+ display: "flex",
70
+ flexDirection: "column",
71
+ gap: 2
72
+ },
73
+ children: [vendorStatus.vendors?.map((vendor, index) => /* @__PURE__ */(0, _jsxRuntime.jsx)(_progressItem.VendorProgressItem, {
74
+ vendor
75
+ }, vendor.title || `vendor-${index}`)), pageInfo?.success_message?.[locale] && isAllCompleted ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
76
+ variant: "h6",
77
+ sx: {
78
+ color: "text.primary",
79
+ mb: 1
80
+ },
81
+ children: pageInfo?.success_message?.[locale]
82
+ }) : null]
83
+ });
84
+ };
29
85
  if (["subscription", "setup"].includes(action)) {
30
86
  if (subscriptions && subscriptions.length > 1) {
31
87
  next = /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Paper, {
@@ -142,7 +198,7 @@ function PaymentSuccess({
142
198
  children: t("payment.checkout.completed.tip", {
143
199
  payee
144
200
  })
145
- }), next]
201
+ }), renderVendorProgress(), next]
146
202
  })
147
203
  });
148
204
  }