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(); const { t } = useTranslation();
return ( return (
<main> <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"> <div className="mx-auto max-w-lg space-y-3 text-center">
<ShieldExclamationIcon className="mx-auto size-20 text-red-600" /> <ShieldExclamationIcon className="mx-auto size-20 text-red-600" />
<h3 className="font-semibold text-4xl text-accent-foreground sm:text-5xl"> <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-3xl">{data.packName}</p>
<p className="text-lg">Cod: &ldquo;{data.packid}&rdquo;</p> <p className="text-lg">Cod: &ldquo;{data.packid}&rdquo;</p>
<p className="font-bold text-4xl"> <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>
<p>{data.packDesc}</p> <p>{data.packDesc}</p>
</div> </div>
@ -130,6 +133,15 @@ export const getServerSideProps = (async (context) => {
type: parsedType.data, type: parsedType.data,
servizioId: parsedServizioId.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({ const flag = await helper.trpc.flags.GetFlagValue.fetch({
id: "STRIPE_DISABLED", id: "STRIPE_DISABLED",
@ -138,7 +150,7 @@ export const getServerSideProps = (async (context) => {
return { return {
props: { props: {
stripeDisabled: flag || false, stripeDisabled: flag || false,
data, data: data.data,
}, },
}; };
} }

View file

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

View file

@ -5,6 +5,7 @@ import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { import {
getPagamentoDataForCheckout, getPagamentoDataForCheckout,
preparePayment, preparePayment,
type PreparedPaymentData,
} from "~/server/controllers/pagamenti.controller"; } from "~/server/controllers/pagamenti.controller";
import { db } from "~/server/db"; import { db } from "~/server/db";
@ -44,6 +45,16 @@ export const pagamentiRouter = createTRPCRouter({
type: z.custom<OrderTypeEnum>(), 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 }) => { .query(async ({ input }) => {
return await preparePayment(input.servizioId, input.type); return await preparePayment(input.servizioId, input.type);
}), }),

View file

@ -27,7 +27,7 @@ export type PreparedPaymentData = {
export const preparePayment = async ( export const preparePayment = async (
servizioId: ServizioServizioId, servizioId: ServizioServizioId,
type: OrderTypeEnum, type: OrderTypeEnum,
): Promise<PreparedPaymentData> => { ): Promise<{success: true, data: PreparedPaymentData} | {success:false, message: string}> => {
try { try {
const servizio = await getServizioById(servizioId); const servizio = await getServizioById(servizioId);
const inactiveOrders = await db const inactiveOrders = await db
@ -43,12 +43,17 @@ export const preparePayment = async (
if (!servizio.isOkAcconto) { if (!servizio.isOkAcconto) {
const acconto = await AccontoSolver(servizio); const acconto = await AccontoSolver(servizio);
if (!acconto.success) { if (!acconto.success) {
throw new Error( return {
`Acconto non trovato per il servizio: ${acconto.message}`, success: false,
); message: `Acconto non trovato per il servizio: ${acconto.message}`,
};
} }
if (!acconto.pack.testo_condizioni) { 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 // servizio sta aspettando attivazione acconto
@ -59,7 +64,7 @@ export const preparePayment = async (
if (preExistingAcconto) { if (preExistingAcconto) {
// acconto già creato, procedere con questo // acconto già creato, procedere con questo
return { return {success: true, data: {
ordine_id: preExistingAcconto.ordine_id, ordine_id: preExistingAcconto.ordine_id,
packid: preExistingAcconto.packid, packid: preExistingAcconto.packid,
amount_cent: preExistingAcconto.amount_cent, amount_cent: preExistingAcconto.amount_cent,
@ -67,7 +72,7 @@ export const preparePayment = async (
packName: acconto.pack.nome_it, packName: acconto.pack.nome_it,
packDesc: acconto.pack.desc_it, packDesc: acconto.pack.desc_it,
condizioni: acconto.pack.testo_condizioni, condizioni: acconto.pack.testo_condizioni,
}; }};
} }
// nessun acconto preesistente, crearne uno nuovo // nessun acconto preesistente, crearne uno nuovo
@ -78,7 +83,7 @@ export const preparePayment = async (
type: OrderTypeEnum.Acconto, type: OrderTypeEnum.Acconto,
userid: servizio.user_id, userid: servizio.user_id,
}); });
return { return {success: true, data: {
ordine_id: ordine.ordine_id, ordine_id: ordine.ordine_id,
packid: acconto.pack.idprezziario, packid: acconto.pack.idprezziario,
amount_cent: acconto.pack.prezzo_cent, amount_cent: acconto.pack.prezzo_cent,
@ -86,36 +91,43 @@ export const preparePayment = async (
packName: acconto.pack.nome_it, packName: acconto.pack.nome_it,
packDesc: acconto.pack.desc_it, packDesc: acconto.pack.desc_it,
condizioni: acconto.pack.testo_condizioni, 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: { case OrderTypeEnum.Saldo: {
if (!servizio.isOkAcconto) { if (!servizio.isOkAcconto) {
throw new Error( return {
"Non è possibile procedere con il pagamento del saldo prima di aver attivato l'acconto", success: false,
); message: "Non è possibile procedere con il pagamento del saldo prima di aver attivato l'acconto",
};
} }
if (!servizio.isOkSaldo) { if (!servizio.isOkSaldo) {
//find saldo pack based on servizio tipologia and permanenza/budget, if tipologia posizione //find saldo pack based on servizio tipologia and permanenza/budget, if tipologia posizione
const saldo = await SaldoSolver(servizio); const saldo = await SaldoSolver(servizio);
if (!saldo.success) { if (!saldo.success) {
throw new Error( return {
`Saldo non trovato per il servizio: ${saldo.message}`, success: false,
); message: `Saldo non trovato per il servizio: ${saldo.message}`,
};
} }
if (!saldo.pack.testo_condizioni) { 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( const preExistingSaldo = inactiveOrders.find(
(ordine) => ordine.type === OrderTypeEnum.Saldo, (ordine) => ordine.type === OrderTypeEnum.Saldo,
); );
if (preExistingSaldo) { if (preExistingSaldo) {
// saldo già creato, procedere con questo // saldo già creato, procedere con questo
return { return{success: true, data: {
ordine_id: preExistingSaldo.ordine_id, ordine_id: preExistingSaldo.ordine_id,
packid: preExistingSaldo.packid, packid: preExistingSaldo.packid,
amount_cent: preExistingSaldo.amount_cent, amount_cent: preExistingSaldo.amount_cent,
@ -123,7 +135,7 @@ export const preparePayment = async (
packName: saldo.pack.nome_it, packName: saldo.pack.nome_it,
packDesc: saldo.pack.desc_it, packDesc: saldo.pack.desc_it,
condizioni: saldo.pack.testo_condizioni, condizioni: saldo.pack.testo_condizioni,
}; }};
} }
// nessun saldo preesistente, crearne uno nuovo // nessun saldo preesistente, crearne uno nuovo
const ordine = await createOrdine({ const ordine = await createOrdine({
@ -133,7 +145,7 @@ export const preparePayment = async (
type: OrderTypeEnum.Saldo, type: OrderTypeEnum.Saldo,
userid: servizio.user_id, userid: servizio.user_id,
}); });
return { return {success: true, data: {
ordine_id: ordine.ordine_id, ordine_id: ordine.ordine_id,
packid: saldo.pack.idprezziario, packid: saldo.pack.idprezziario,
amount_cent: saldo.pack.prezzo_cent, amount_cent: saldo.pack.prezzo_cent,
@ -141,18 +153,20 @@ export const preparePayment = async (
packName: saldo.pack.nome_it, packName: saldo.pack.nome_it,
packDesc: saldo.pack.desc_it, packDesc: saldo.pack.desc_it,
condizioni: saldo.pack.testo_condizioni, 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: { case OrderTypeEnum.Consulenza: {
if (!servizio.isOkAcconto || !servizio.isOkSaldo) { if (!servizio.isOkAcconto || !servizio.isOkSaldo) {
throw new Error( return {
"Non è possibile procedere con il pagamento della consulenza prima di aver attivato acconto e saldo", success: false,
); message: "Non è possibile procedere con il pagamento della consulenza prima di aver attivato acconto e saldo",
};
} }
if (!servizio.isOkConsulenza) { if (!servizio.isOkConsulenza) {
const existingConsulenza = inactiveOrders.find( const existingConsulenza = inactiveOrders.find(
@ -164,12 +178,20 @@ export const preparePayment = async (
existingConsulenza.packid, existingConsulenza.packid,
); );
if (!consulenzaPack) { if (!consulenzaPack) {
throw new Error("Pack consulenza non trovato"); return {
success: false,
message: `Pack della consulenza non trovato: ${existingConsulenza.packid}`,
}
} }
if (!consulenzaPack.testo_condizioni) { 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 { return {
success: true,
data: {
ordine_id: existingConsulenza.ordine_id, ordine_id: existingConsulenza.ordine_id,
packid: existingConsulenza.packid, packid: existingConsulenza.packid,
amount_cent: existingConsulenza.amount_cent, amount_cent: existingConsulenza.amount_cent,
@ -177,21 +199,103 @@ export const preparePayment = async (
packName: consulenzaPack.nome_it, packName: consulenzaPack.nome_it,
packDesc: consulenzaPack.desc_it, packDesc: consulenzaPack.desc_it,
condizioni: consulenzaPack.testo_condizioni, 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( return {
"Il servizio non ha un pacchetto di consulenza, è necessario inserirlo prima di procedere", success: false,
); message: "Il servizio non ha un pacchetto di consulenza, è necessario inserirlo prima di procedere",
};
} }
throw new Error( return {
"Il servizio ha già una consulenza attiva, non è possibile preparare un nuovo pagamento", 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: default:
throw new Error("Tipo di ordine non valido"); throw new Error(`Tipo di ordine non valido: ${type satisfies never}`);
} }
} catch (e) { } catch (e) {
throw new TRPCError({ throw new TRPCError({