@blocklet/payment-react
Version:
Reusable react components for payment kit v2
372 lines (369 loc) • 10.7 kB
JavaScript
import { jsx, jsxs } from "react/jsx-runtime";
import bridge from "@arcblock/bridge";
import useArcblockBrowser from "@arcblock/react-hooks/lib/useBrowser";
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
import { Box, Grow, Link, Paper, Stack, styled, Typography } from "@mui/material";
import { useEffect, useRef, useState } from "react";
import { joinURL } from "ufo";
import { Button } from "@arcblock/ux";
import { usePaymentContext } from "../contexts/payment.js";
import { VendorPlaceholder, VendorProgressItem } from "./progress-item.js";
export default function PaymentSuccess({
mode,
pageInfo = {},
vendorCount = 0,
sessionId = "",
message,
action,
payee,
invoiceId = "",
subscriptionId = "",
subscriptions = []
}) {
const { t, locale } = useLocaleContext();
const { prefix, api } = usePaymentContext();
const [vendorStatus, setVendorStatus] = useState(null);
const [isAllCompleted, setIsAllCompleted] = useState(false);
const [hasFailed, setHasFailed] = useState(false);
const timerRef = useRef(Date.now());
const browser = useArcblockBrowser();
const inArcsphere = browser?.arcSphere;
let next = null;
useEffect(() => {
if (vendorCount === 0 || !sessionId) return void 0;
const fetchVendorStatus = async (interval2) => {
try {
const response = await api.get(joinURL(prefix, `/api/vendors/order/${sessionId}/status`), {});
const needCheckError = Date.now() - timerRef.current > 6 * 1e3;
const allCompleted = response.data?.vendors?.every((vendor) => vendor.progress >= 100);
const hasAnyFailed = response.data?.vendors?.some(
(vendor) => vendor.status === "failed" || needCheckError && !!vendor.error && !!vendor.error_message
);
if (hasAnyFailed || allCompleted) {
clearInterval(interval2);
}
setHasFailed(hasAnyFailed);
setIsAllCompleted(!hasAnyFailed && allCompleted);
setVendorStatus(response.data);
} catch (error) {
console.error("Failed to fetch vendor status:", error);
}
};
fetchVendorStatus();
const interval = setInterval(() => {
fetchVendorStatus(interval);
}, 5e3);
return () => clearInterval(interval);
}, [vendorCount, api, prefix, sessionId]);
useEffect(() => {
if (inArcsphere && isAllCompleted) {
try {
bridge?.call?.("arc__toast", { text: t("payment.checkout.vendor.arcSphereToast") });
const timer = setTimeout(() => {
bridge?.call?.("arcClosePage");
}, 3e3);
return () => clearTimeout(timer);
} catch (error) {
}
}
return void 0;
}, [isAllCompleted, t, locale, inArcsphere]);
const renderPlaceholders = () => {
const placeholders = [];
for (let i = 0; i < vendorCount; i++) {
placeholders.push(/* @__PURE__ */ jsx(VendorPlaceholder, {}, `placeholder-${i}`));
}
return placeholders;
};
const renderVendors = () => {
if (!vendorStatus) return renderPlaceholders();
return vendorStatus.vendors?.map((vendor, index) => {
return /* @__PURE__ */ jsx(VendorProgressItem, { vendor }, vendor.title || `vendor-${index}`);
});
};
const renderVendorProgress = () => {
if (vendorCount === 0) return null;
return /* @__PURE__ */ jsxs(
Paper,
{
elevation: 0,
sx: {
p: 3,
backgroundColor: "grey.50",
borderRadius: 2,
width: "100%",
mt: 2,
display: "flex",
flexDirection: "column",
gap: 2
},
children: [
renderVendors(),
hasFailed ? /* @__PURE__ */ jsx(Typography, { variant: "h6", sx: { color: "warning.main", mb: 1 }, children: t("payment.checkout.vendor.failedMsg") }) : null,
pageInfo?.success_message?.[locale] && isAllCompleted ? /* @__PURE__ */ jsx(Typography, { variant: "h6", sx: { color: "text.primary", mb: 1 }, children: pageInfo?.success_message?.[locale] }) : null
]
}
);
};
if (["subscription", "setup"].includes(action)) {
if (subscriptions && subscriptions.length > 1) {
next = /* @__PURE__ */ jsx(
Paper,
{
elevation: 0,
sx: {
p: 3,
backgroundColor: "grey.50",
borderRadius: 2,
width: "100%",
mt: 2,
display: "flex",
flexDirection: "column",
gap: 2
},
children: subscriptions.map((subscription) => /* @__PURE__ */ jsxs(
Box,
{
sx: {
display: "flex",
alignItems: "center",
justifyContent: "space-between"
},
children: [
/* @__PURE__ */ jsx(
Typography,
{
variant: "body2",
sx: {
color: "text.secondary",
fontWeight: 500
},
children: subscription.description
}
),
/* @__PURE__ */ jsx(
Box,
{
sx: {
flex: 1,
borderBottom: "1px dashed",
borderColor: "grey.300",
mx: 2
}
}
),
/* @__PURE__ */ jsx(Button, { variant: "text", color: "primary", size: "small", children: /* @__PURE__ */ jsx(
Link,
{
href: joinURL(prefix, `/customer/subscription/${subscription.id}`),
sx: { color: "text.secondary" },
children: t("payment.checkout.next.view")
}
) })
]
},
subscription.id
))
}
);
} else if (subscriptionId) {
next = /* @__PURE__ */ jsx(Button, { variant: "outlined", color: "primary", sx: { mt: 2 }, children: /* @__PURE__ */ jsx(Link, { href: joinURL(prefix, `/customer/subscription/${subscriptionId}`), children: t("payment.checkout.next.subscription", { payee }) }) });
}
} else if (invoiceId) {
next = /* @__PURE__ */ jsx(
Typography,
{
sx: {
textAlign: "center",
mt: 2
},
children: /* @__PURE__ */ jsx(Link, { href: joinURL(prefix, `/customer/invoice/${invoiceId}`), children: t("payment.checkout.next.invoice", { payee }) })
}
);
}
return /* @__PURE__ */ jsx(Grow, { in: true, children: /* @__PURE__ */ jsxs(
Stack,
{
direction: "column",
sx: {
alignItems: "center",
justifyContent: mode === "standalone" ? "center" : "flex-start",
height: mode === "standalone" ? "fit-content" : 300
},
children: [
/* @__PURE__ */ jsx(Div, { children: /* @__PURE__ */ jsxs("div", { className: "check-icon", children: [
/* @__PURE__ */ jsx("span", { className: "icon-line line-tip" }),
/* @__PURE__ */ jsx("span", { className: "icon-line line-long" }),
/* @__PURE__ */ jsx("div", { className: "icon-circle" }),
/* @__PURE__ */ jsx("div", { className: "icon-fix" })
] }) }),
/* @__PURE__ */ jsx(
Typography,
{
variant: "h5",
sx: {
color: "text.primary",
mb: 3
},
children: message
}
),
/* @__PURE__ */ jsx(
Typography,
{
variant: "body1",
sx: {
color: "text.secondary",
textAlign: "center",
fontSize: "14px"
},
children: t("payment.checkout.completed.tip", { payee })
}
),
renderVendorProgress(),
next
]
}
) });
}
const Div = styled("div")`
width: 80px;
height: 115px;
.check-icon {
width: 80px;
height: 80px;
position: relative;
border-radius: 50%;
box-sizing: content-box;
border: 4px solid ${(props) => props.theme.palette.success.main};
}
.check-icon::before {
top: 3px;
left: -2px;
width: 30px;
transform-origin: 100% 50%;
border-radius: 100px 0 0 100px;
}
.check-icon::after {
top: 0;
left: 30px;
width: 60px;
transform-origin: 0 50%;
border-radius: 0 100px 100px 0;
animation: rotate-circle 4.25s ease-in;
}
.check-icon::before,
.check-icon::after {
content: '';
height: 100px;
position: absolute;
background: ${(props) => props.theme.palette.background.default};
transform: rotate(-45deg);
}
.check-icon .icon-line {
height: 5px;
background-color: ${(props) => props.theme.palette.success.main};
display: block;
border-radius: 2px;
position: absolute;
z-index: 10;
}
.check-icon .icon-line.line-tip {
top: 46px;
left: 14px;
width: 25px;
transform: rotate(45deg);
animation: icon-line-tip 0.75s;
}
.check-icon .icon-line.line-long {
top: 38px;
right: 8px;
width: 47px;
transform: rotate(-45deg);
animation: icon-line-long 0.75s;
}
.check-icon .icon-circle {
top: -4px;
left: -4px;
z-index: 10;
width: 80px;
height: 80px;
border-radius: 50%;
position: absolute;
box-sizing: content-box;
border: 4px solid rgba(76, 175, 80, 0.5);
}
.check-icon .icon-fix {
top: 8px;
width: 5px;
left: 26px;
z-index: 1;
height: 85px;
position: absolute;
transform: rotate(-45deg);
background-color: ${(props) => props.theme.palette.background.default};
}
@keyframes rotate-circle {
0% {
transform: rotate(-45deg);
}
5% {
transform: rotate(-45deg);
}
12% {
transform: rotate(-405deg);
}
100% {
transform: rotate(-405deg);
}
}
@keyframes icon-line-tip {
0% {
width: 0;
left: 1px;
top: 19px;
}
54% {
width: 0;
left: 1px;
top: 19px;
}
70% {
width: 50px;
left: -8px;
top: 37px;
}
84% {
width: 17px;
left: 21px;
top: 48px;
}
100% {
width: 25px;
left: 14px;
top: 45px;
}
}
@keyframes icon-line-long {
0% {
width: 0;
right: 46px;
top: 54px;
}
65% {
width: 0;
right: 46px;
top: 54px;
}
84% {
width: 55px;
right: 0px;
top: 35px;
}
100% {
width: 47px;
right: 8px;
top: 38px;
}
}
`;