@blocklet/payment-react
Version:
Reusable react components for payment kit v2
122 lines (121 loc) • 4.27 kB
JavaScript
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 });
}