@blocklet/payment-react
Version:
Reusable react components for payment kit v2
127 lines (126 loc) • 3.94 kB
JavaScript
import { jsx } from "react/jsx-runtime";
import { useRequest, useSetState } from "ahooks";
import noop from "lodash/noop";
import { useEffect } from "react";
import { joinURL } from "ufo";
import { ReactGA } from "@arcblock/ux/lib/withTracker";
import api from "../libs/api.js";
import { getPrefix, mergeExtraParams } from "../libs/util.js";
import Payment from "../payment/index.js";
import { PaymentThemeProvider } from "../theme/index.js";
import DonationForm from "../payment/donation-form.js";
const promises = {};
const startFromPaymentLink = (id, params) => {
if (!promises[id]) {
promises[id] = api.post(`/api/checkout-sessions/start/${id}?${mergeExtraParams(params)}`).then((res) => res?.data).finally(() => {
setTimeout(() => {
delete promises[id];
}, 3e3);
});
}
return promises[id];
};
const fetchCheckoutSession = async (id) => {
const { data } = await api.get(`/api/checkout-sessions/retrieve/${id}`);
return data;
};
export default function CheckoutForm({
id,
mode = "inline",
onPaid = noop,
onError = console.error,
onChange,
goBack,
extraParams = {},
action = "",
theme = "default",
formType = "payment",
...restProps
}) {
if (!id.startsWith("plink_") && !id.startsWith("cs_")) {
throw new Error("Either a checkoutSession or a paymentLink id is required.");
}
const type = id.startsWith("plink_") ? "paymentLink" : "checkoutSession";
const [state, setState] = useSetState({ completed: false, appError: null });
const { error: apiError, data } = useRequest(
() => type === "paymentLink" ? startFromPaymentLink(id, extraParams) : fetchCheckoutSession(id)
);
useEffect(() => {
if (type === "paymentLink" && mode === "standalone" && data) {
window.history.replaceState(
null,
"",
joinURL(getPrefix(), `/checkout/pay/${data.checkoutSession.id}?${mergeExtraParams(extraParams)}`)
);
}
}, [type, mode, data, extraParams]);
const handlePaid = (result) => {
setState({ completed: true });
onPaid?.(result);
const paySuccessEvent = {
action: "paySuccess",
// @ts-ignore 后续升级的话就会报错了,移除这个 lint 即可
mode: data?.checkoutSession?.mode,
success: true
};
ReactGA.event(paySuccessEvent.action, paySuccessEvent);
};
const handleError = (err) => {
console.error(err);
setState({ appError: err });
onError?.(err);
const payFailedEvent = {
action: "payFailed",
// @ts-ignore后续升级的话就会报错了,移除这个 lint 即可
mode: data?.checkoutSession?.mode,
errorMessage: err.message,
success: false
};
ReactGA.event(payFailedEvent.action, payFailedEvent);
};
const Checkout = formType === "donation" ? /* @__PURE__ */ jsx(
DonationForm,
{
checkoutSession: data?.checkoutSession,
paymentMethods: data?.paymentMethods,
paymentIntent: data?.paymentIntent,
paymentLink: data?.paymentLink,
customer: data?.customer,
completed: state.completed,
error: apiError,
onPaid: handlePaid,
onError: handleError,
onChange,
goBack,
mode,
action,
id,
...restProps
}
) : /* @__PURE__ */ jsx(
Payment,
{
checkoutSession: data?.checkoutSession,
paymentMethods: data?.paymentMethods,
paymentIntent: data?.paymentIntent,
paymentLink: data?.paymentLink,
customer: data?.customer,
completed: state.completed,
error: apiError,
onPaid: handlePaid,
onError: handleError,
onChange,
goBack,
mode,
action,
...restProps
}
);
if (theme === "inherit") {
return Checkout;
}
if (theme && typeof theme === "object") {
return /* @__PURE__ */ jsx(PaymentThemeProvider, { theme, children: Checkout });
}
return /* @__PURE__ */ jsx(PaymentThemeProvider, { children: Checkout });
}