import { TRPCError } from "@trpc/server"; import { env } from "~/env"; import stripe from "~/lib/stripe"; import OrderTypeEnum from "~/schemas/public/OrderTypeEnum"; import type { Ordini, OrdiniOrdineId } from "~/schemas/public/Ordini"; import PaymentStatusEnum from "~/schemas/public/PaymentStatusEnum"; import type { TestiEStringheStingaId } from "~/schemas/public/TestiEStringhe"; import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; import { db } from "~/server/db"; import { NewMail } from "~/server/services/mailer"; import { getUser } from "~/server/services/user.service"; import { addEventToQueue } from "./event_queue.controller"; export const stripeHealthCheck = async () => { try { const endpoints = await stripe.webhookEndpoints.list(); return { success: true, endpoints, secretKey: env.STRIPE_SECRET_KEY, publicKey: env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY, }; } catch (e) { console.error("Stripe health check failed:", e); return { success: false, endpoints: null, secretKey: env.STRIPE_SECRET_KEY, publicKey: env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY, }; } }; export const whIntentCreatedHandler = async ({ pIntentId, ordineId, }: { pIntentId: Ordini["intent_id"]; ordineId: OrdiniOrdineId; }) => { try { return await db .updateTable("ordini") .set({ intent_id: pIntentId, paymentstatus: PaymentStatusEnum.processing, }) .where("ordine_id", "=", ordineId) .returning("ordine_id") .execute(); } catch (error) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: `Error whIntentCreatedHandler: ${(error as Error).message}`, }); } }; export const whIntentSucceededHandler = async ({ pIntentId, ordineId, pm_id, }: { pIntentId: Ordini["intent_id"]; ordineId: OrdiniOrdineId; pm_id: string; }) => { try { const paymentMethod = await stripe.paymentMethods.retrieve(pm_id); await db.transaction().execute(async (trx) => { const ordine = await trx .updateTable("ordini") .set({ paid_at: new Date(), paymentmethod: paymentMethod.type, paymentstatus: PaymentStatusEnum.success, isActive: true, }) .where("intent_id", "=", pIntentId) .where("ordine_id", "=", ordineId) .returning(["servizio_id", "ordine_id", "userid", "type", "created_at"]) .executeTakeFirstOrThrow(() => { throw new Error( `Payment not found - intent_id: ${pIntentId}, ordineId: ${ordineId}`, ); }); const servizio = await trx .selectFrom("servizio") .select(["servizio.tipologia", "servizio.servizio_id"]) .where("servizio_id", "=", ordine.servizio_id) .executeTakeFirstOrThrow(() => { throw new Error( `Servizio not found - servizio_id: ${ordine.servizio_id}`, ); }); const utente = await getUser({ db: trx, userId: ordine.userid, }); const lock_expires = new Date(Date.now() + 60 * 1000); // Lock for 1 minute switch (ordine.type) { case OrderTypeEnum.Acconto: await trx .updateTable("servizio") .where("servizio_id", "=", servizio.servizio_id) .set({ decorrenza: new Date(), isOkAcconto: true, }) .execute(); await addEventToQueue({ data: { tipo: "email", data: { template: { mailType: "servizioAttivato", }, mail: { subject: "Pagamento Confermato - Acconto Infoalloggi.it", to: utente.email, attachments: [ { filename: `condizioni_contrattuali_${ordine.created_at.toISOString()}.pdf`, //ATTENTION: hardcoded condizioni key generate: { type: "condizioni", stringId: "CONDIZIONI_CERCHI" as TestiEStringheStingaId, }, encoding: "base64", contentType: "application/pdf", }, { filename: `ricevuta_cortesia_${ordine.ordine_id}.pdf`, generate: { type: "ricevuta_cortesia", ordineId: ordine.ordine_id, }, encoding: "base64", contentType: "application/pdf", }, ], }, userId: utente.id, }, }, lock_expires, }); //SERVIZIO ATTIVATO break; case OrderTypeEnum.Saldo: await trx .updateTable("servizio") .where("servizio_id", "=", servizio.servizio_id) .set({ isOkConsulenza: servizio.tipologia === TipologiaPosizioneEnum.Transitorio, isOkSaldo: true, }) .execute(); await addEventToQueue({ data: { tipo: "email", data: { template: { mailType: "pagamentoConferma", }, mail: { subject: "Pagamento Confermato - Saldo Infoalloggi.it", to: utente.email, attachments: [ { filename: `ricevuta_cortesia_${ordine.ordine_id}.pdf`, generate: { type: "ricevuta_cortesia", ordineId: ordine.ordine_id, }, encoding: "base64", contentType: "application/pdf", }, ], }, userId: utente.id, }, }, lock_expires, }); //PAGAMENTO SALDO CONFERMATO break; case OrderTypeEnum.Consulenza: await trx .updateTable("servizio") .where("servizio_id", "=", servizio.servizio_id) .set({ isOkConsulenza: true, }) .execute(); await addEventToQueue({ data: { tipo: "email", data: { template: { mailType: "pagamentoConferma", }, mail: { subject: "Pagamento Confermato - Consulenza Infoalloggi.it", to: utente.email, attachments: [ { filename: `ricevuta_cortesia_${ordine.ordine_id}.pdf`, generate: { type: "ricevuta_cortesia", ordineId: ordine.ordine_id, }, encoding: "base64", contentType: "application/pdf", }, ], }, userId: utente.id, }, }, lock_expires, }); //CONSULENZA ATTIVATA break; case OrderTypeEnum.Altro: // TODO: Handle other types await addEventToQueue({ data: { tipo: "email", data: { template: { mailType: "pagamentoConferma", }, mail: { subject: "Pagamento Confermato - Infoalloggi.it", to: utente.email, attachments: [ { filename: `ricevuta_cortesia_${ordine.ordine_id}.pdf`, generate: { type: "ricevuta_cortesia", ordineId: ordine.ordine_id, }, encoding: "base64", contentType: "application/pdf", }, ], }, userId: utente.id, }, }, lock_expires, }); break; } }); return true; } catch (error) { // Enhanced error logging console.error("whIntentSucceededHandler error:", { pIntentId, ordineId, pm_id, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, }); throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: `Error updating payment intent: ${(error as Error).message}`, cause: error, }); } }; export const whIntentFailedHandler = async ({ pIntentId, }: { pIntentId: Ordini["intent_id"]; }) => { const userId = await db .updateTable("ordini") .set({ paymentstatus: PaymentStatusEnum.failed, }) .returning("userid") .where("intent_id", "=", pIntentId) .execute(); if (userId[0]) { const utente = await getUser({ db, userId: userId[0].userid, }); await NewMail({ template: { mailType: "pagamentoErrore", }, mail: { subject: "Pagamento Fallito - Infoalloggi.it", to: utente.email, }, userId: utente.id, }); } return true; };