2025-08-04 17:45:44 +02:00
|
|
|
import { Elements, useStripe } from "@stripe/react-stripe-js";
|
|
|
|
|
import { loadStripe } from "@stripe/stripe-js";
|
|
|
|
|
import { ArrowLeft } from "lucide-react";
|
|
|
|
|
import type { GetServerSideProps } from "next";
|
2025-08-28 18:27:07 +02:00
|
|
|
import Link from "next/link";
|
|
|
|
|
import { useRouter } from "next/router";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { useEffect, useState } from "react";
|
2025-08-28 18:27:07 +02:00
|
|
|
import PaymentStatus from "~/components/payment_status";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { ProgressRedirect } from "~/components/progress_redirect";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { Button, buttonVariants } from "~/components/ui/button";
|
|
|
|
|
import { env } from "~/env.mjs";
|
|
|
|
|
import { cn } from "~/lib/utils";
|
|
|
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
|
|
|
|
import PaymentStatusEnum from "~/schemas/public/PaymentStatusEnum";
|
|
|
|
|
import { api } from "~/utils/api";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
|
|
|
|
type AcquistoProcessingProps = {
|
2025-08-28 18:27:07 +02:00
|
|
|
client_secret: string;
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getServerSideProps = (async (context) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
if (
|
|
|
|
|
!context.query.payment_intent_client_secret ||
|
|
|
|
|
typeof context.query.payment_intent_client_secret !== "string"
|
|
|
|
|
) {
|
|
|
|
|
return {
|
|
|
|
|
redirect: {
|
|
|
|
|
destination: "/500",
|
|
|
|
|
permanent: false,
|
2025-08-29 16:18:32 +02:00
|
|
|
statusCode: 500,
|
2025-08-28 18:27:07 +02:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return {
|
|
|
|
|
props: {
|
|
|
|
|
client_secret: context.query.payment_intent_client_secret,
|
|
|
|
|
},
|
|
|
|
|
};
|
2025-08-04 17:45:44 +02:00
|
|
|
}) satisfies GetServerSideProps<AcquistoProcessingProps>;
|
|
|
|
|
|
|
|
|
|
export default function AcquistoProcessing({
|
2025-08-28 18:27:07 +02:00
|
|
|
client_secret,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: AcquistoProcessingProps) {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { t } = useTranslation();
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const stripePromise = loadStripe(env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY);
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<div className="mx-auto my-5 w-full max-w-lg space-y-8">
|
|
|
|
|
<h3 className="my-8 text-center text-xl font-bold tracking-wide text-neutral-700 uppercase dark:text-white">
|
|
|
|
|
{t.acquisto_elaborazione.titolo}
|
|
|
|
|
</h3>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div>
|
|
|
|
|
<Elements
|
|
|
|
|
options={{
|
|
|
|
|
appearance: {
|
|
|
|
|
theme: "stripe",
|
|
|
|
|
},
|
|
|
|
|
clientSecret: client_secret,
|
|
|
|
|
}}
|
|
|
|
|
stripe={stripePromise}
|
|
|
|
|
>
|
|
|
|
|
<CompletePage secret={client_secret} />
|
|
|
|
|
</Elements>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
|
|
|
|
const CompletePage = ({ secret }: { secret: string }) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const stripe = useStripe();
|
|
|
|
|
const utils = api.useUtils();
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
const [status, setStatus] = useState<
|
|
|
|
|
"success" | "processing" | "failed" | "internal_error" | "not_found"
|
|
|
|
|
>("processing");
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
useEffect(() => {
|
|
|
|
|
const fetchPaymentIntent = async () => {
|
|
|
|
|
if (!stripe) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
try {
|
|
|
|
|
const { paymentIntent } = await stripe.retrievePaymentIntent(secret);
|
|
|
|
|
if (!paymentIntent) {
|
|
|
|
|
setStatus("internal_error");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const payment = await utils.pagamenti.getPaymentFromIntent.fetch({
|
|
|
|
|
pIntentId: paymentIntent.id,
|
|
|
|
|
});
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
if (!payment) {
|
|
|
|
|
setStatus("not_found");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
if (paymentIntent.status === "succeeded") {
|
|
|
|
|
if (payment.paymentstatus !== PaymentStatusEnum.success) {
|
|
|
|
|
setStatus("internal_error");
|
|
|
|
|
}
|
|
|
|
|
setStatus("success");
|
|
|
|
|
} else if (paymentIntent.status === "processing") {
|
|
|
|
|
if (payment.paymentstatus !== PaymentStatusEnum.processing) {
|
|
|
|
|
setStatus("internal_error");
|
|
|
|
|
}
|
|
|
|
|
setStatus("processing");
|
|
|
|
|
} else {
|
|
|
|
|
setStatus("failed");
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error retrieving payment intent:", error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
void fetchPaymentIntent();
|
|
|
|
|
}, [stripe, secret]);
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
if (!status) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<div className="space-y-4 p-4">
|
|
|
|
|
{status === "success" && (
|
|
|
|
|
<>
|
|
|
|
|
<PaymentStatus
|
|
|
|
|
message={t.acquisto_elaborazione.success_desc}
|
2025-08-29 16:18:32 +02:00
|
|
|
status="success"
|
|
|
|
|
title={t.acquisto_elaborazione.success_title}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<ProgressRedirect href="/area-riservata/dashboard" />
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{status === "processing" && (
|
|
|
|
|
<>
|
|
|
|
|
<PaymentStatus
|
|
|
|
|
message={t.acquisto_elaborazione.processing_desc}
|
2025-08-29 16:18:32 +02:00
|
|
|
status="processing"
|
|
|
|
|
title={t.acquisto_elaborazione.processing_title}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
<Link
|
|
|
|
|
className={cn("w-full", buttonVariants({ variant: "info" }))}
|
2025-08-29 16:18:32 +02:00
|
|
|
href="/area-riservata/dashboard"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
{t.acquisto_elaborazione.to_dashboard}
|
|
|
|
|
</Link>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{status === "failed" && (
|
|
|
|
|
<>
|
|
|
|
|
<PaymentStatus
|
|
|
|
|
message={t.acquisto_elaborazione.failed_desc}
|
2025-08-29 16:18:32 +02:00
|
|
|
status="error"
|
|
|
|
|
title={t.acquisto_elaborazione.failed_title}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
<Link
|
|
|
|
|
className={cn("w-full", buttonVariants({ variant: "default" }))}
|
2025-08-29 16:18:32 +02:00
|
|
|
href="/area-riservata/chat"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
{t.acquisto_elaborazione.to_chat}
|
|
|
|
|
</Link>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{status === "internal_error" && (
|
|
|
|
|
<>
|
|
|
|
|
<PaymentStatus
|
|
|
|
|
message={t.acquisto_elaborazione.internal_error_desc}
|
2025-08-29 16:18:32 +02:00
|
|
|
status="error"
|
|
|
|
|
title={t.acquisto_elaborazione.internal_error_title}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
<Link
|
|
|
|
|
className={cn("w-full", buttonVariants({ variant: "default" }))}
|
2025-08-29 16:18:32 +02:00
|
|
|
href="/area-riservata/chat"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
{t.acquisto_elaborazione.to_chat}
|
|
|
|
|
</Link>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{status === "not_found" && (
|
|
|
|
|
<>
|
|
|
|
|
<PaymentStatus
|
|
|
|
|
message={t.acquisto_elaborazione.not_found_desc}
|
2025-08-29 16:18:32 +02:00
|
|
|
status="error"
|
|
|
|
|
title={t.acquisto_elaborazione.not_found_title}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
<Button
|
|
|
|
|
className="w-full"
|
2025-08-29 16:18:32 +02:00
|
|
|
onClick={() => router.back()}
|
2025-08-28 18:27:07 +02:00
|
|
|
variant="secondary"
|
|
|
|
|
>
|
|
|
|
|
<ArrowLeft />
|
|
|
|
|
{t.indietro}
|
|
|
|
|
</Button>
|
|
|
|
|
<Link
|
|
|
|
|
className={cn("w-full", buttonVariants({ variant: "default" }))}
|
2025-08-29 16:18:32 +02:00
|
|
|
href="/area-riservata/chat"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
{t.acquisto_elaborazione.to_chat}
|
|
|
|
|
</Link>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|