feat: update payment preparation logic to include 'Rinnovo' type and handle errors gracefully

This commit is contained in:
Marco Pedone 2026-03-08 15:33:50 +01:00
parent 8716d90148
commit 73245f3564
7 changed files with 182 additions and 51 deletions

View file

@ -1 +0,0 @@
ALTER TABLE IF EXISTS public.ordini DROP COLUMN IF EXISTS condizioni;

View file

@ -0,0 +1,4 @@
ALTER TABLE IF EXISTS public.ordini DROP COLUMN IF EXISTS condizioni;
ALTER TYPE public."OrderTypeEnum"
ADD VALUE IF NOT EXISTS 'Rinnovo' AFTER 'Altro';

View file

@ -7,7 +7,7 @@ export default function Custom500() {
const { t } = useTranslation();
return (
<main>
<div className="mx-auto flex h-screen max-w-screen-xl items-center justify-start px-4 md:px-8">
<div className="mx-auto flex h-screen max-w-7xl items-center justify-start px-4 md:px-8">
<div className="mx-auto max-w-lg space-y-3 text-center">
<ShieldExclamationIcon className="mx-auto size-20 text-red-600" />
<h3 className="font-semibold text-4xl text-accent-foreground sm:text-5xl">

View file

@ -36,7 +36,10 @@ const PreviewPurchasePage: NextPageWithLayout<PreviewPurchaseProps> = ({
<p className="text-3xl">{data.packName}</p>
<p className="text-lg">Cod: &ldquo;{data.packid}&rdquo;</p>
<p className="font-bold text-4xl">
{formatCurrency(data.amount_cent / 100)} {data.sconto > 0 && <span className="text-green-500">(Sconto: {data.sconto}%)</span>}
{formatCurrency(data.amount_cent / 100)}{" "}
{data.sconto > 0 && (
<span className="text-green-500">(Sconto: {data.sconto}%)</span>
)}
</p>
<p>{data.packDesc}</p>
</div>
@ -130,6 +133,15 @@ export const getServerSideProps = (async (context) => {
type: parsedType.data,
servizioId: parsedServizioId.data,
});
if (!data.success) {
console.error("Error preparing payment", data.message);
return {
redirect: {
destination: `/500`,
permanent: false,
},
};
}
const flag = await helper.trpc.flags.GetFlagValue.fetch({
id: "STRIPE_DISABLED",
@ -138,7 +150,7 @@ export const getServerSideProps = (async (context) => {
return {
props: {
stripeDisabled: flag || false,
data,
data: data.data,
},
};
}

View file

@ -7,6 +7,7 @@ enum OrderTypeEnum {
Saldo = 'Saldo',
Consulenza = 'Consulenza',
Altro = 'Altro',
Rinnovo = 'Rinnovo',
};
export default OrderTypeEnum;

View file

@ -5,6 +5,7 @@ import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import {
getPagamentoDataForCheckout,
preparePayment,
type PreparedPaymentData,
} from "~/server/controllers/pagamenti.controller";
import { db } from "~/server/db";
@ -44,6 +45,16 @@ export const pagamentiRouter = createTRPCRouter({
type: z.custom<OrderTypeEnum>(),
}),
)
.output(z.discriminatedUnion("", [
z.object({
success: z.literal(true),
data: z.custom<PreparedPaymentData>(),
}),
z.object({
success: z.literal(false),
message: z.string(),
}),
]),)
.query(async ({ input }) => {
return await preparePayment(input.servizioId, input.type);
}),

View file

@ -27,7 +27,7 @@ export type PreparedPaymentData = {
export const preparePayment = async (
servizioId: ServizioServizioId,
type: OrderTypeEnum,
): Promise<PreparedPaymentData> => {
): Promise<{success: true, data: PreparedPaymentData} | {success:false, message: string}> => {
try {
const servizio = await getServizioById(servizioId);
const inactiveOrders = await db
@ -43,12 +43,17 @@ export const preparePayment = async (
if (!servizio.isOkAcconto) {
const acconto = await AccontoSolver(servizio);
if (!acconto.success) {
throw new Error(
`Acconto non trovato per il servizio: ${acconto.message}`,
);
return {
success: false,
message: `Acconto non trovato per il servizio: ${acconto.message}`,
};
}
if (!acconto.pack.testo_condizioni) {
throw new Error("Condizioni non trovate per acconto");
return {
success: false,
message: `Condizioni non trovate per acconto: ${acconto.pack.nome_it}`,
};
}
// servizio sta aspettando attivazione acconto
@ -59,7 +64,7 @@ export const preparePayment = async (
if (preExistingAcconto) {
// acconto già creato, procedere con questo
return {
return {success: true, data: {
ordine_id: preExistingAcconto.ordine_id,
packid: preExistingAcconto.packid,
amount_cent: preExistingAcconto.amount_cent,
@ -67,7 +72,7 @@ export const preparePayment = async (
packName: acconto.pack.nome_it,
packDesc: acconto.pack.desc_it,
condizioni: acconto.pack.testo_condizioni,
};
}};
}
// nessun acconto preesistente, crearne uno nuovo
@ -78,7 +83,7 @@ export const preparePayment = async (
type: OrderTypeEnum.Acconto,
userid: servizio.user_id,
});
return {
return {success: true, data: {
ordine_id: ordine.ordine_id,
packid: acconto.pack.idprezziario,
amount_cent: acconto.pack.prezzo_cent,
@ -86,36 +91,43 @@ export const preparePayment = async (
packName: acconto.pack.nome_it,
packDesc: acconto.pack.desc_it,
condizioni: acconto.pack.testo_condizioni,
};
}};
}
return{
success: false,
message: "Il servizio ha già un acconto attivo, non è possibile preparare un nuovo pagamento"
}
throw new Error(
"Il servizio ha già un acconto attivo, non è possibile preparare un nuovo pagamento",
);
}
case OrderTypeEnum.Saldo: {
if (!servizio.isOkAcconto) {
throw new Error(
"Non è possibile procedere con il pagamento del saldo prima di aver attivato l'acconto",
);
return {
success: false,
message: "Non è possibile procedere con il pagamento del saldo prima di aver attivato l'acconto",
};
}
if (!servizio.isOkSaldo) {
//find saldo pack based on servizio tipologia and permanenza/budget, if tipologia posizione
const saldo = await SaldoSolver(servizio);
if (!saldo.success) {
throw new Error(
`Saldo non trovato per il servizio: ${saldo.message}`,
);
return {
success: false,
message: `Saldo non trovato per il servizio: ${saldo.message}`,
};
}
if (!saldo.pack.testo_condizioni) {
throw new Error("Condizioni non trovate per saldo");
return {
success: false,
message: `Condizioni non trovate per saldo: ${saldo.pack.nome_it}`,
};
}
const preExistingSaldo = inactiveOrders.find(
(ordine) => ordine.type === OrderTypeEnum.Saldo,
);
if (preExistingSaldo) {
// saldo già creato, procedere con questo
return {
return{success: true, data: {
ordine_id: preExistingSaldo.ordine_id,
packid: preExistingSaldo.packid,
amount_cent: preExistingSaldo.amount_cent,
@ -123,7 +135,7 @@ export const preparePayment = async (
packName: saldo.pack.nome_it,
packDesc: saldo.pack.desc_it,
condizioni: saldo.pack.testo_condizioni,
};
}};
}
// nessun saldo preesistente, crearne uno nuovo
const ordine = await createOrdine({
@ -133,7 +145,7 @@ export const preparePayment = async (
type: OrderTypeEnum.Saldo,
userid: servizio.user_id,
});
return {
return {success: true, data: {
ordine_id: ordine.ordine_id,
packid: saldo.pack.idprezziario,
amount_cent: saldo.pack.prezzo_cent,
@ -141,18 +153,20 @@ export const preparePayment = async (
packName: saldo.pack.nome_it,
packDesc: saldo.pack.desc_it,
condizioni: saldo.pack.testo_condizioni,
};
}};
}
return{
success: false,
message: "Il servizio ha già un saldo attivo, non è possibile preparare un nuovo pagamento"
}
throw new Error(
"Il servizio ha già un saldo attivo, non è possibile preparare un nuovo pagamento",
);
}
case OrderTypeEnum.Consulenza: {
if (!servizio.isOkAcconto || !servizio.isOkSaldo) {
throw new Error(
"Non è possibile procedere con il pagamento della consulenza prima di aver attivato acconto e saldo",
);
return {
success: false,
message: "Non è possibile procedere con il pagamento della consulenza prima di aver attivato acconto e saldo",
};
}
if (!servizio.isOkConsulenza) {
const existingConsulenza = inactiveOrders.find(
@ -164,34 +178,124 @@ export const preparePayment = async (
existingConsulenza.packid,
);
if (!consulenzaPack) {
throw new Error("Pack consulenza non trovato");
return {
success: false,
message: `Pack della consulenza non trovato: ${existingConsulenza.packid}`,
}
}
if (!consulenzaPack.testo_condizioni) {
throw new Error("Condizioni non trovate per consulenza");
return {
success: false,
message: `Condizioni non trovate per consulenza: ${consulenzaPack.nome_it}`,
};
}
return {
ordine_id: existingConsulenza.ordine_id,
packid: existingConsulenza.packid,
amount_cent: existingConsulenza.amount_cent,
sconto: existingConsulenza.sconto,
packName: consulenzaPack.nome_it,
packDesc: consulenzaPack.desc_it,
condizioni: consulenzaPack.testo_condizioni,
success: true,
data: {
ordine_id: existingConsulenza.ordine_id,
packid: existingConsulenza.packid,
amount_cent: existingConsulenza.amount_cent,
sconto: existingConsulenza.sconto,
packName: consulenzaPack.nome_it,
packDesc: consulenzaPack.desc_it,
condizioni: consulenzaPack.testo_condizioni,
},
};
}
//todo vedere se oltre a emettere un errore si vuole anche passare un messaggio alla pagina di errore
throw new Error(
"Il servizio non ha un pacchetto di consulenza, è necessario inserirlo prima di procedere",
);
return {
success: false,
message: "Il servizio non ha un pacchetto di consulenza, è necessario inserirlo prima di procedere",
};
}
throw new Error(
"Il servizio ha già una consulenza attiva, non è possibile preparare un nuovo pagamento",
);
return {
success: false,
message: "Il servizio ha già una consulenza attiva, non è possibile preparare un nuovo pagamento",
};
}
case OrderTypeEnum.Rinnovo : {
const existingRinnovo = inactiveOrders.find(
(ordine) => ordine.type === OrderTypeEnum.Rinnovo,
);
if (existingRinnovo) {
// rinnovo già creato, procedere con questo
const rinnovoPack = await getPrezziarioByIdHandler(
existingRinnovo.packid,
);
if (!rinnovoPack) {
return {
success: false,
message: `Pack del rinnovo non trovato: ${existingRinnovo.packid}`,
}
}
if (!rinnovoPack.testo_condizioni) {
return {
success: false,
message: `Condizioni non trovate per rinnovo: ${rinnovoPack.nome_it}`,
};
}
return {
success: true,
data: {
ordine_id: existingRinnovo.ordine_id,
packid: existingRinnovo.packid,
amount_cent: existingRinnovo.amount_cent,
sconto: existingRinnovo.sconto,
packName: rinnovoPack.nome_it,
packDesc: rinnovoPack.desc_it,
condizioni: rinnovoPack.testo_condizioni,
},
};
}
return {
success: false,
message: "Il servizio non ha un pacchetto di rinnovo, è necessario inserirlo prima di procedere",
};
}
case OrderTypeEnum.Altro : {
const existingAltro = inactiveOrders.find(
(ordine) => ordine.type === OrderTypeEnum.Altro,
);
if (existingAltro) {
// altro già creato, procedere con questo
const altroPack = await getPrezziarioByIdHandler(
existingAltro.packid,
);
if (!altroPack) {
return {
success: false,
message: `Pack dell'altro non trovato: ${existingAltro.packid}`,
}
}
if (!altroPack.testo_condizioni) {
return {
success: false,
message: `Condizioni non trovate per altro: ${altroPack.nome_it}`,
};
}
return {
success: true,
data: {
ordine_id: existingAltro.ordine_id,
packid: existingAltro.packid,
amount_cent: existingAltro.amount_cent,
sconto: existingAltro.sconto,
packName: altroPack.nome_it,
packDesc: altroPack.desc_it,
condizioni: altroPack.testo_condizioni,
},
};
}
return {
success: false,
message: "La preparazione del pagamento per questa tipologia non è supportata",
};
}
default:
throw new Error("Tipo di ordine non valido");
throw new Error(`Tipo di ordine non valido: ${type satisfies never}`);
}
} catch (e) {
throw new TRPCError({