From 73245f35648b010ffe11f0ffde679d382bffe1b5 Mon Sep 17 00:00:00 2001 From: Marco Pedone Date: Sun, 8 Mar 2026 15:33:50 +0100 Subject: [PATCH] feat: update payment preparation logic to include 'Rinnovo' type and handle errors gracefully --- apps/db/migrations/35_removecondizioni.up.sql | 1 - apps/db/migrations/35_rinnovi.up.sql | 4 + apps/infoalloggi/src/pages/500.tsx | 2 +- .../pages/servizio/pagamento/[servizioId].tsx | 16 +- .../src/schemas/public/OrderTypeEnum.ts | 1 + .../src/server/api/routers/pagamenti.ts | 11 + .../controllers/pagamenti.controller.ts | 198 +++++++++++++----- 7 files changed, 182 insertions(+), 51 deletions(-) delete mode 100644 apps/db/migrations/35_removecondizioni.up.sql create mode 100644 apps/db/migrations/35_rinnovi.up.sql diff --git a/apps/db/migrations/35_removecondizioni.up.sql b/apps/db/migrations/35_removecondizioni.up.sql deleted file mode 100644 index d58c005..0000000 --- a/apps/db/migrations/35_removecondizioni.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE IF EXISTS public.ordini DROP COLUMN IF EXISTS condizioni; \ No newline at end of file diff --git a/apps/db/migrations/35_rinnovi.up.sql b/apps/db/migrations/35_rinnovi.up.sql new file mode 100644 index 0000000..625937b --- /dev/null +++ b/apps/db/migrations/35_rinnovi.up.sql @@ -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'; \ No newline at end of file diff --git a/apps/infoalloggi/src/pages/500.tsx b/apps/infoalloggi/src/pages/500.tsx index dfc9234..860f846 100644 --- a/apps/infoalloggi/src/pages/500.tsx +++ b/apps/infoalloggi/src/pages/500.tsx @@ -7,7 +7,7 @@ export default function Custom500() { const { t } = useTranslation(); return (
-
+

diff --git a/apps/infoalloggi/src/pages/servizio/pagamento/[servizioId].tsx b/apps/infoalloggi/src/pages/servizio/pagamento/[servizioId].tsx index 7834dfb..957ad3a 100644 --- a/apps/infoalloggi/src/pages/servizio/pagamento/[servizioId].tsx +++ b/apps/infoalloggi/src/pages/servizio/pagamento/[servizioId].tsx @@ -36,7 +36,10 @@ const PreviewPurchasePage: NextPageWithLayout = ({

{data.packName}

Cod: “{data.packid}”

- {formatCurrency(data.amount_cent / 100)} {data.sconto > 0 && (Sconto: {data.sconto}%)} + {formatCurrency(data.amount_cent / 100)}{" "} + {data.sconto > 0 && ( + (Sconto: {data.sconto}%) + )}

{data.packDesc}

@@ -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, }, }; } diff --git a/apps/infoalloggi/src/schemas/public/OrderTypeEnum.ts b/apps/infoalloggi/src/schemas/public/OrderTypeEnum.ts index f1752d2..d85c107 100644 --- a/apps/infoalloggi/src/schemas/public/OrderTypeEnum.ts +++ b/apps/infoalloggi/src/schemas/public/OrderTypeEnum.ts @@ -7,6 +7,7 @@ enum OrderTypeEnum { Saldo = 'Saldo', Consulenza = 'Consulenza', Altro = 'Altro', + Rinnovo = 'Rinnovo', }; export default OrderTypeEnum; diff --git a/apps/infoalloggi/src/server/api/routers/pagamenti.ts b/apps/infoalloggi/src/server/api/routers/pagamenti.ts index 6465450..c7e0f16 100644 --- a/apps/infoalloggi/src/server/api/routers/pagamenti.ts +++ b/apps/infoalloggi/src/server/api/routers/pagamenti.ts @@ -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(), }), ) + .output(z.discriminatedUnion("", [ + z.object({ + success: z.literal(true), + data: z.custom(), + }), + z.object({ + success: z.literal(false), + message: z.string(), + }), + ]),) .query(async ({ input }) => { return await preparePayment(input.servizioId, input.type); }), diff --git a/apps/infoalloggi/src/server/controllers/pagamenti.controller.ts b/apps/infoalloggi/src/server/controllers/pagamenti.controller.ts index 8c13655..752aaa2 100644 --- a/apps/infoalloggi/src/server/controllers/pagamenti.controller.ts +++ b/apps/infoalloggi/src/server/controllers/pagamenti.controller.ts @@ -27,7 +27,7 @@ export type PreparedPaymentData = { export const preparePayment = async ( servizioId: ServizioServizioId, type: OrderTypeEnum, -): Promise => { +): 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, - }; + }}; } - - throw new Error( - "Il servizio ha già un acconto attivo, non è possibile preparare un nuovo pagamento", - ); + return{ + success: false, + message: "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({