UNPKG

@blocklet/payment-react

Version:

Reusable react components for payment kit v2

372 lines (369 loc) 10.7 kB
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; } } `;