"use client";
import Link from "next/link";
import { useEffect, useState } from "react";
import QRCode from "react-qr-code";
import z from "zod";
import type { PaymentPageProps } from "@calcom/features/ee/payments/pages/payment";
import { useBookingSuccessRedirect } from "@calcom/lib/bookingSuccessRedirect";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useCopy } from "@calcom/lib/hooks/useCopy";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { Button } from "@calcom/ui/components/button";
import { Spinner } from "@calcom/ui/components/icon";
import { showToast } from "@calcom/ui/components/toast";
interface IAlbyPaymentComponentProps {
payment: {
// Will be parsed on render
data: unknown;
};
paymentPageProps: PaymentPageProps;
}
// Create zod schema for data
const PaymentAlbyDataSchema = z.object({
invoice: z
.object({
paymentRequest: z.string(),
})
.required(),
});
export const AlbyPaymentComponent = (props: IAlbyPaymentComponentProps) => {
const { payment } = props;
const { data } = payment;
const [showQRCode, setShowQRCode] = useState(window.webln === undefined);
const [isPaying, setPaying] = useState(false);
const { copyToClipboard, isCopied } = useCopy();
const wrongUrl = (
<>
Couldn't obtain payment URL
>
);
const parsedData = PaymentAlbyDataSchema.safeParse(data);
if (!parsedData.success || !parsedData.data?.invoice?.paymentRequest) {
return wrongUrl;
}
const paymentRequest = parsedData.data.invoice.paymentRequest;
return (
{isPaying &&
}
{!isPaying && (
<>
{!showQRCode && (
{window.webln && (
)}
)}
{showQRCode && (
<>
Click or scan the invoice below to pay
Don't have a lightning wallet?
>
)}
>
)}
Powered by
);
};
type PaymentCheckerProps = PaymentPageProps;
function PaymentChecker(props: PaymentCheckerProps) {
// TODO: move booking success code to a common lib function
// TODO: subscribe rather than polling
const searchParams = useCompatSearchParams();
const bookingSuccessRedirect = useBookingSuccessRedirect();
const utils = trpc.useUtils();
const { t } = useLocale();
useEffect(() => {
if (searchParams === null) {
return;
}
// use closure to ensure non-nullability
const sp = searchParams;
const interval = setInterval(() => {
(async () => {
if (props.booking.status === "ACCEPTED") {
return;
}
const { booking: bookingResult } = await utils.viewer.bookings.find.fetch({
bookingUid: props.booking.uid,
});
if (bookingResult?.paid) {
showToast("Payment successful", "success");
const params: {
uid: string;
email: string | null;
location: string;
} = {
uid: props.booking.uid,
email: sp.get("email"),
location: t("web_conferencing_details_to_follow"),
};
bookingSuccessRedirect({
successRedirectUrl: props.eventType.successRedirectUrl,
query: params,
booking: props.booking,
forwardParamsSuccessRedirect: props.eventType.forwardParamsSuccessRedirect,
});
}
})();
}, 1000);
return () => clearInterval(interval);
}, [
bookingSuccessRedirect,
props.booking,
props.booking.id,
props.booking.status,
props.eventType.id,
props.eventType.successRedirectUrl,
props.eventType.forwardParamsSuccessRedirect,
props.payment.success,
searchParams,
t,
utils.viewer.bookings,
]);
return null;
}