UNPKG

@blocklet/payment-react

Version:

Reusable react components for payment kit v2

122 lines (121 loc) 4.27 kB
import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import { useEffect, useState } from "react"; import { useRequest } from "ahooks"; import { joinURL } from "ufo"; import { Box } from "@mui/material"; import { CheckoutProvider, useSessionContext, useSubmitFeature } from "@blocklet/payment-react-headless"; import api from "../libs/api.js"; import { getPrefix, mergeExtraParams } from "../libs/util.js"; import { PaymentThemeProvider } from "../theme/index.js"; import { useMobile } from "../hooks/mobile.js"; import CheckoutLayout from "./layouts/checkout-layout.js"; import ScenarioRouter from "./panels/left/scenario-router.js"; import PaymentPanel from "./panels/right/payment-panel.js"; import CheckoutDialogs from "./components/dialogs/checkout-dialogs.js"; import LoadingView from "./views/loading-view.js"; import ErrorView from "./views/error-view.js"; import SuccessView from "./views/success-view.js"; const plinkPromises = {}; function startFromPaymentLink(id, params) { if (!plinkPromises[id]) { plinkPromises[id] = api.post(`/api/checkout-sessions/start/${id}?${mergeExtraParams(params)}`).then((res) => res?.data).finally(() => { setTimeout(() => { delete plinkPromises[id]; }, 3e3); }); } return plinkPromises[id]; } function CheckoutRouter({ onPaid = void 0, onError = void 0, mode = "inline" }) { const { isLoading, error, errorCode, session } = useSessionContext(); const submit = useSubmitFeature(); const { isMobile } = useMobile(); useEffect(() => { if (error && onError) { onError(new Error(error)); } }, [error, onError]); useEffect(() => { if (submit.status === "completed" && submit.result && onPaid) { onPaid(submit.result); } }, [submit.status, submit.result, onPaid]); if (isLoading) { return /* @__PURE__ */ jsx(LoadingView, { mode }); } if (error) { return /* @__PURE__ */ jsx(ErrorView, { error, errorCode, mode }); } const isCompleted = submit.status === "completed"; const mobileCompleted = isCompleted && isMobile; return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx( CheckoutLayout, { left: mobileCompleted ? null : /* @__PURE__ */ jsx( Box, { sx: { flex: 1, display: "flex", flexDirection: "column", ...isCompleted ? { opacity: 0.45, pointerEvents: "none", filter: "grayscale(0.3)", transition: "all 0.5s ease" } : {} }, children: /* @__PURE__ */ jsx(ScenarioRouter, {}) } ), right: isCompleted ? /* @__PURE__ */ jsx(SuccessView, { submit, session }) : /* @__PURE__ */ jsx(PaymentPanel, {}), mode } ), !isCompleted && /* @__PURE__ */ jsx(CheckoutDialogs, {}) ] }); } export default function CheckoutV2({ id, onPaid, onError, theme = "default", mode = "inline", extraParams = {} }) { if (!id.startsWith("plink_") && !id.startsWith("cs_")) { throw new Error("Either a checkoutSession or a paymentLink id is required."); } const isPaymentLink = id.startsWith("plink_"); const [resolvedSessionId, setResolvedSessionId] = useState(isPaymentLink ? null : id); useRequest( async () => { if (!isPaymentLink) return null; const data = await startFromPaymentLink(id, extraParams); const csId = data?.checkoutSession?.id; if (csId) { setResolvedSessionId(csId); if (mode === "standalone") { window.history.replaceState( null, "", joinURL(getPrefix(), `/checkout/pay/${csId}?${mergeExtraParams(extraParams)}`) ); } } return data; }, { ready: isPaymentLink } ); if (!resolvedSessionId) { return /* @__PURE__ */ jsx(LoadingView, { mode }); } const content = /* @__PURE__ */ jsx(CheckoutProvider, { sessionId: resolvedSessionId, children: /* @__PURE__ */ jsx(CheckoutRouter, { onPaid, onError, mode }) }); if (theme === "inherit") { return content; } if (theme && typeof theme === "object") { return /* @__PURE__ */ jsx(PaymentThemeProvider, { theme, children: content }); } return /* @__PURE__ */ jsx(PaymentThemeProvider, { children: content }); }