- Removed AcquistoProcessing component and replaced it with a new CheckoutForm component for better separation of concerns. - Added new payment checkout page to handle payment intents and display payment status. - Updated server-side logic to create payment intents and retrieve payment data for checkout. - Enhanced error handling and user feedback during payment processing. - Introduced new utility functions for fetching payment data and managing Stripe integration.
173 lines
4.3 KiB
TypeScript
173 lines
4.3 KiB
TypeScript
import { ArrowRight, ExternalLink } from "lucide-react";
|
|
import type { GetServerSideProps } from "next";
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/router";
|
|
import { AreaRiservataLayout } from "~/components/Layout";
|
|
import { LoadingPage } from "~/components/loading";
|
|
import { Status500 } from "~/components/status-page";
|
|
import { Button } from "~/components/ui/button";
|
|
import { formatCurrency } from "~/lib/utils";
|
|
import type { NextPageWithLayout } from "~/pages/_app";
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
|
import type { OrdiniOrdineId } from "~/schemas/public/Ordini";
|
|
import type { PrezziarioIdprezziario } from "~/schemas/public/Prezziario";
|
|
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
|
|
import { zOrdineId } from "~/server/utils/zod_types";
|
|
import { api } from "~/utils/api";
|
|
|
|
type ServizioPurchaseProps = {
|
|
ordineId: OrdiniOrdineId;
|
|
};
|
|
|
|
const ServicePurchasePage: NextPageWithLayout<ServizioPurchaseProps> = ({
|
|
ordineId,
|
|
}: ServizioPurchaseProps) => {
|
|
const router = useRouter();
|
|
const { data, isLoading } = api.servizio.getServizioPaymentData.useQuery({
|
|
ordineId,
|
|
});
|
|
|
|
if (isLoading) {
|
|
return <LoadingPage />;
|
|
}
|
|
if (!data) {
|
|
return <Status500 />;
|
|
}
|
|
return (
|
|
<div className="mx-auto w-full max-w-6xl grow space-y-4 p-2 sm:px-8 sm:py-4">
|
|
<PricePreparation
|
|
onProcedi={async () =>
|
|
await router.push(`/servizio/pagamento-checkout/${data.payment.id}`)
|
|
}
|
|
packId={data.ordine.packid}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const PricePreparation = ({
|
|
packId,
|
|
onProcedi,
|
|
}: {
|
|
packId: PrezziarioIdprezziario;
|
|
onProcedi: () => void;
|
|
}) => {
|
|
const { data: prezziario, isLoading } =
|
|
api.prezziario.getPrezzoPerServizio.useQuery({
|
|
idprezziario: packId,
|
|
});
|
|
const { t } = useTranslation();
|
|
return (
|
|
<div className="mx-auto mt-4 max-w-2xl p-2 sm:mt-10">
|
|
{isLoading || !prezziario ? (
|
|
<>
|
|
<h3 className="font-semibold text-2xl">{t.acquisto.preparazione}</h3>
|
|
<LoadingPage />
|
|
</>
|
|
) : (
|
|
<div className="space-y-8">
|
|
<div className="space-y-6">
|
|
<p className="text-3xl">{prezziario.nome_it}</p>
|
|
<p className="text-lg">
|
|
Cod: “{prezziario.idprezziario}”
|
|
</p>
|
|
<p className="font-bold text-4xl">
|
|
{formatCurrency(prezziario.prezzo_cent / 100)}
|
|
</p>
|
|
<p>{prezziario.desc_it}</p>
|
|
</div>
|
|
|
|
{prezziario.testo_condizioni && (
|
|
<Link
|
|
aria-label="Leggi le condizioni"
|
|
className="block w-fit"
|
|
href={`/servizio/condizioni/${prezziario.testo_condizioni}`}
|
|
target="_blank"
|
|
>
|
|
<Button className="w-full text-sm">
|
|
<span>{t.acquisto.leggi_condizioni}</span>
|
|
<ExternalLink />
|
|
</Button>
|
|
</Link>
|
|
)}
|
|
|
|
<Button
|
|
className="w-full text-lg"
|
|
disabled={isLoading || !prezziario}
|
|
onClick={onProcedi}
|
|
size="xl"
|
|
variant="info"
|
|
>
|
|
<span>{t.acquisto.procedi_pagamento}</span> <ArrowRight />
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
ServicePurchasePage.getLayout = function getLayout(page) {
|
|
return <AreaRiservataLayout noSidebar>{page}</AreaRiservataLayout>;
|
|
};
|
|
|
|
export default ServicePurchasePage;
|
|
|
|
export const getServerSideProps = (async (context) => {
|
|
const id = context.params?.ordineId;
|
|
|
|
if (typeof id !== "string") {
|
|
console.error("Error: ordineId is not a string");
|
|
return {
|
|
redirect: {
|
|
destination: "/500",
|
|
permanent: false,
|
|
},
|
|
};
|
|
}
|
|
|
|
const parsed = zOrdineId.safeParse(id);
|
|
if (!parsed.success) {
|
|
console.error("Error parsing ordineId", parsed.error);
|
|
return {
|
|
redirect: {
|
|
destination: "/500",
|
|
permanent: false,
|
|
},
|
|
};
|
|
}
|
|
|
|
const access_token = context.req.cookies.access_token;
|
|
|
|
const helper = await TrpcAuthedFetchingIstance({ access_token });
|
|
if (helper) {
|
|
const isAwaitingPayment =
|
|
await helper.trpc.servizio.isOrdineAwaitingPayment.fetch({
|
|
ordineId: parsed.data,
|
|
});
|
|
if (!isAwaitingPayment) {
|
|
return {
|
|
redirect: {
|
|
destination: "/area-riservata/dashboard",
|
|
permanent: false,
|
|
},
|
|
};
|
|
}
|
|
|
|
await helper.trpc.servizio.getServizioPaymentData.prefetch({
|
|
ordineId: parsed.data,
|
|
});
|
|
|
|
return {
|
|
props: {
|
|
ordineId: parsed.data,
|
|
trpcState: helper.trpc.dehydrate(),
|
|
},
|
|
};
|
|
}
|
|
return {
|
|
redirect: {
|
|
destination: "/500",
|
|
permanent: false,
|
|
},
|
|
};
|
|
}) satisfies GetServerSideProps<ServizioPurchaseProps>;
|