- Implemented FormEditOrder component for editing orders with validation and submission handling. - Created SchedaAnnuncioPage for displaying printable announcement details. - Added BonificoManualePage for manual bank transfer payment instructions. - Developed PagamentoPage for Stripe payment processing with PaymentIntent creation. - Introduced PreviewPurchasePage for preparing payment with service details. - Added pagamenti.controller for handling payment preparation and retrieval of payment data.
191 lines
5.1 KiB
TypeScript
191 lines
5.1 KiB
TypeScript
import { ArrowLeft } from "lucide-react";
|
|
import type { GetServerSideProps } from "next";
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/router";
|
|
import PaymentStatus from "~/components/payment_status";
|
|
import { ProgressRedirect } from "~/components/progress_redirect";
|
|
import { Button, buttonVariants } from "~/components/ui/button";
|
|
import stripe from "~/lib/stripe";
|
|
import { cn } from "~/lib/utils";
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
|
import PaymentStatusEnum from "~/schemas/public/PaymentStatusEnum";
|
|
import type { ServizioServizioId } from "~/schemas/public/Servizio";
|
|
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
|
|
|
|
enum AcquistoStatusEnum {
|
|
success = "success",
|
|
processing = "processing",
|
|
failed = "failed",
|
|
internal_error = "internal_error",
|
|
not_found = "not_found",
|
|
}
|
|
|
|
type AcquistoProcessingProps = {
|
|
servizioId: ServizioServizioId | null;
|
|
status: AcquistoStatusEnum;
|
|
};
|
|
|
|
export const getServerSideProps = (async (context) => {
|
|
const paymentIntentId = context.query?.payment_intent;
|
|
if (!paymentIntentId || typeof paymentIntentId !== "string") {
|
|
return {
|
|
redirect: {
|
|
destination: "/500",
|
|
permanent: false,
|
|
},
|
|
};
|
|
}
|
|
|
|
const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId);
|
|
if (!paymentIntent) {
|
|
return {
|
|
props: {
|
|
servizioId: null,
|
|
status: AcquistoStatusEnum.internal_error,
|
|
},
|
|
};
|
|
}
|
|
|
|
const access_token = context.req.cookies.access_token;
|
|
|
|
const helper = await TrpcAuthedFetchingIstance({ access_token });
|
|
if (helper) {
|
|
const payment = await helper.trpc.pagamenti.getPaymentFromIntent.fetch({
|
|
pIntentId: paymentIntent.id,
|
|
});
|
|
|
|
if (!payment) {
|
|
return {
|
|
props: {
|
|
servizioId: null,
|
|
status: AcquistoStatusEnum.not_found,
|
|
},
|
|
};
|
|
}
|
|
|
|
let resultStatus: AcquistoStatusEnum;
|
|
if (paymentIntent.status === "succeeded") {
|
|
if (payment.paymentstatus !== PaymentStatusEnum.success) {
|
|
resultStatus = AcquistoStatusEnum.internal_error;
|
|
}
|
|
resultStatus = AcquistoStatusEnum.success;
|
|
} else if (paymentIntent.status === "processing") {
|
|
if (payment.paymentstatus !== PaymentStatusEnum.processing) {
|
|
resultStatus = AcquistoStatusEnum.internal_error;
|
|
}
|
|
resultStatus = AcquistoStatusEnum.processing;
|
|
} else {
|
|
resultStatus = AcquistoStatusEnum.failed;
|
|
}
|
|
return {
|
|
props: {
|
|
servizioId: payment.servizio_id,
|
|
status: resultStatus,
|
|
},
|
|
};
|
|
}
|
|
|
|
return {
|
|
redirect: {
|
|
destination: "/500",
|
|
permanent: false,
|
|
},
|
|
};
|
|
}) satisfies GetServerSideProps<AcquistoProcessingProps>;
|
|
|
|
export default function AcquistoProcessing({
|
|
servizioId,
|
|
status,
|
|
}: AcquistoProcessingProps) {
|
|
const { t } = useTranslation();
|
|
const router = useRouter();
|
|
return (
|
|
<div className="mx-auto my-5 w-full max-w-lg space-y-8">
|
|
<h3 className="my-8 text-center font-bold text-neutral-700 text-xl uppercase tracking-wide dark:text-white">
|
|
{t.acquisto_elaborazione.titolo}
|
|
</h3>
|
|
|
|
<div className="space-y-4 p-4">
|
|
{status === AcquistoStatusEnum.success && (
|
|
<>
|
|
<PaymentStatus
|
|
message={t.acquisto_elaborazione.success_desc}
|
|
status="success"
|
|
title={t.acquisto_elaborazione.success_title}
|
|
/>
|
|
|
|
<ProgressRedirect href={`/area-riservata/servizio/${servizioId}`} />
|
|
</>
|
|
)}
|
|
{status === AcquistoStatusEnum.processing && (
|
|
<>
|
|
<PaymentStatus
|
|
message={t.acquisto_elaborazione.processing_desc}
|
|
status="processing"
|
|
title={t.acquisto_elaborazione.processing_title}
|
|
/>
|
|
<Link
|
|
className={cn("w-full", buttonVariants({ variant: "info" }))}
|
|
href="/area-riservata/dashboard"
|
|
>
|
|
{t.acquisto_elaborazione.to_dashboard}
|
|
</Link>
|
|
</>
|
|
)}
|
|
{status === AcquistoStatusEnum.failed && (
|
|
<>
|
|
<PaymentStatus
|
|
message={t.acquisto_elaborazione.failed_desc}
|
|
status="error"
|
|
title={t.acquisto_elaborazione.failed_title}
|
|
/>
|
|
<Link
|
|
className={cn("w-full", buttonVariants({ variant: "default" }))}
|
|
href="/area-riservata/chat"
|
|
>
|
|
{t.acquisto_elaborazione.to_chat}
|
|
</Link>
|
|
</>
|
|
)}
|
|
{status === AcquistoStatusEnum.internal_error && (
|
|
<>
|
|
<PaymentStatus
|
|
message={t.acquisto_elaborazione.internal_error_desc}
|
|
status="error"
|
|
title={t.acquisto_elaborazione.internal_error_title}
|
|
/>
|
|
<Link
|
|
className={cn("w-full", buttonVariants({ variant: "default" }))}
|
|
href="/area-riservata/chat"
|
|
>
|
|
{t.acquisto_elaborazione.to_chat}
|
|
</Link>
|
|
</>
|
|
)}
|
|
{status === AcquistoStatusEnum.not_found && (
|
|
<>
|
|
<PaymentStatus
|
|
message={t.acquisto_elaborazione.not_found_desc}
|
|
status="error"
|
|
title={t.acquisto_elaborazione.not_found_title}
|
|
/>
|
|
<Button
|
|
className="w-full"
|
|
onClick={() => router.back()}
|
|
variant="secondary"
|
|
>
|
|
<ArrowLeft />
|
|
{t.indietro}
|
|
</Button>
|
|
<Link
|
|
className={cn("w-full", buttonVariants({ variant: "default" }))}
|
|
href="/area-riservata/chat"
|
|
>
|
|
{t.acquisto_elaborazione.to_chat}
|
|
</Link>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|