@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.
- package/.aigne/doc-smith/config.yaml +2 -2
- package/.aigne/doc-smith/upload-cache.yaml +381 -0
- package/docs/components-business-auto-topup.md +128 -179
- package/docs/components-business-overdue-invoice-payment.md +108 -143
- package/docs/components-business-resume-subscription.md +117 -104
- package/docs/components-business.md +12 -36
- package/docs/components-checkout-checkout-donate.md +209 -149
- package/docs/components-checkout-checkout-form.md +115 -136
- package/docs/components-checkout-checkout-table.md +92 -172
- package/docs/components-checkout.md +43 -109
- package/docs/components-history-credit-grants-list.md +45 -70
- package/docs/components-history-credit-transactions-list.md +57 -67
- package/docs/components-history-invoice-list.md +58 -52
- package/docs/components-history-payment-list.md +19 -40
- package/docs/components-history.md +42 -67
- package/docs/components-ui-form-elements-address-form.md +37 -65
- package/docs/components-ui-form-elements-country-select.md +80 -59
- package/docs/components-ui-form-elements-currency-selector.md +57 -73
- package/docs/components-ui-form-elements-phone-input.md +90 -112
- package/docs/components-ui-form-elements.md +46 -80
- package/docs/components-ui-payment-summary.md +71 -119
- package/docs/components-ui-pricing-table.md +117 -204
- package/docs/components-ui.md +59 -32
- package/docs/components.md +89 -62
- package/docs/getting-started.md +36 -63
- package/docs/guides-theming.md +122 -84
- package/docs/guides-utilities.md +107 -145
- package/docs/guides.md +7 -84
- package/docs/hooks-use-mobile.md +50 -36
- package/docs/hooks-use-subscription.md +72 -89
- package/docs/hooks.md +12 -82
- package/docs/overview.md +45 -52
- package/docs/providers-donate-provider.md +73 -95
- package/docs/providers-payment-provider.md +115 -169
- package/docs/providers.md +27 -86
- package/es/locales/en.js +7 -0
- package/es/locales/zh.js +8 -1
- package/es/payment/index.js +3 -0
- package/es/payment/progress-item.d.ts +12 -0
- package/es/payment/progress-item.js +78 -0
- package/es/payment/success.d.ts +4 -1
- package/es/payment/success.js +55 -3
- package/lib/locales/en.js +7 -0
- package/lib/locales/zh.js +8 -1
- package/lib/payment/index.js +3 -0
- package/lib/payment/progress-item.d.ts +12 -0
- package/lib/payment/progress-item.js +107 -0
- package/lib/payment/success.d.ts +4 -1
- package/lib/payment/success.js +59 -3
- package/package.json +3 -3
- package/src/locales/en.tsx +7 -0
- package/src/locales/zh.tsx +8 -1
- package/src/payment/index.tsx +6 -0
- package/src/payment/progress-item.tsx +107 -0
- package/src/payment/success.tsx +88 -3
package/docs/providers.md
CHANGED
|
@@ -1,101 +1,42 @@
|
|
|
1
1
|
# Providers
|
|
2
2
|
|
|
3
|
-
Providers are
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
style.fill: "#DDF0FF"
|
|
13
|
-
"CheckoutForm"
|
|
14
|
-
"CustomerInvoiceList"
|
|
10
|
+
Your-Application: {
|
|
11
|
+
shape: rectangle
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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",
|
package/es/payment/index.js
CHANGED
|
@@ -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
|
+
}
|
package/es/payment/success.d.ts
CHANGED
|
@@ -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 {};
|
package/es/payment/success.js
CHANGED
|
@@ -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,
|
|
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",
|
package/lib/payment/index.js
CHANGED
|
@@ -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
|
+
}
|
package/lib/payment/success.d.ts
CHANGED
|
@@ -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 {};
|
package/lib/payment/success.js
CHANGED
|
@@ -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
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/payment-react",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.6",
|
|
4
4
|
"description": "Reusable react components for payment kit v2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@babel/core": "^7.27.4",
|
|
95
95
|
"@babel/preset-env": "^7.27.2",
|
|
96
96
|
"@babel/preset-react": "^7.27.1",
|
|
97
|
-
"@blocklet/payment-types": "1.20.
|
|
97
|
+
"@blocklet/payment-types": "1.20.6",
|
|
98
98
|
"@storybook/addon-essentials": "^7.6.20",
|
|
99
99
|
"@storybook/addon-interactions": "^7.6.20",
|
|
100
100
|
"@storybook/addon-links": "^7.6.20",
|
|
@@ -125,5 +125,5 @@
|
|
|
125
125
|
"vite-plugin-babel": "^1.3.1",
|
|
126
126
|
"vite-plugin-node-polyfills": "^0.23.0"
|
|
127
127
|
},
|
|
128
|
-
"gitHead": "
|
|
128
|
+
"gitHead": "d04ed7906bd1e13240198623aa0d4eef4db8886e"
|
|
129
129
|
}
|