feat: update payment preparation logic to include 'Rinnovo' type and handle errors gracefully
This commit is contained in:
parent
8716d90148
commit
73245f3564
7 changed files with 182 additions and 51 deletions
|
|
@ -1 +0,0 @@
|
||||||
ALTER TABLE IF EXISTS public.ordini DROP COLUMN IF EXISTS condizioni;
|
|
||||||
4
apps/db/migrations/35_rinnovi.up.sql
Normal file
4
apps/db/migrations/35_rinnovi.up.sql
Normal 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';
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
|
|
@ -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: “{data.packid}”</p>
|
<p className="text-lg">Cod: “{data.packid}”</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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -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,34 +178,124 @@ 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 {
|
||||||
ordine_id: existingConsulenza.ordine_id,
|
success: true,
|
||||||
packid: existingConsulenza.packid,
|
data: {
|
||||||
amount_cent: existingConsulenza.amount_cent,
|
ordine_id: existingConsulenza.ordine_id,
|
||||||
sconto: existingConsulenza.sconto,
|
packid: existingConsulenza.packid,
|
||||||
packName: consulenzaPack.nome_it,
|
amount_cent: existingConsulenza.amount_cent,
|
||||||
packDesc: consulenzaPack.desc_it,
|
sconto: existingConsulenza.sconto,
|
||||||
condizioni: consulenzaPack.testo_condizioni,
|
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(
|
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({
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue