import { TRPCError } from "@trpc/server"; import Stripe from "stripe"; import { env } from "~/env"; import OrderTypeEnum from "~/schemas/public/OrderTypeEnum"; import PaymentStatusEnum from "~/schemas/public/PaymentStatusEnum"; import type { Payments, PaymentsId } from "~/schemas/public/Payments"; import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; import { db } from "~/server/db"; import { NewMail } from "~/server/services/mailer"; import { getUser } from "~/server/services/user.service"; const stripeApi = new Stripe(env.STRIPE_SECRET_KEY); export const createIntentHandler = async ({ paymentId, }: { paymentId: PaymentsId; }) => { try { const payment = await db .selectFrom("payments") .selectAll("payments") .innerJoin("users", "users.id", "payments.userid") .select("users.email") .innerJoin("ordini", "ordini.ordine_id", "payments.ordine_id") .innerJoin("prezziario", "prezziario.idprezziario", "ordini.packid") .select(["prezziario.nome_it", "prezziario.desc_it"]) .where("payments.id", "=", paymentId) .executeTakeFirstOrThrow(); const paymentIntent = await stripeApi.paymentIntents.create({ amount: payment.amount_cent, //payment_method_configuration: "pmc_1P4NfjILe4KoQRqXYChFCTFj", automatic_payment_methods: { enabled: true, }, currency: "eur", description: payment.nome_it, metadata: { paymentId, servizioId: payment.servizio_id, userId: payment.userid, }, receipt_email: payment.email, }); return { clientSecret: paymentIntent.client_secret, descrizione: payment.desc_it, prezzo: payment.amount_cent, titolo: payment.nome_it, }; } catch (error) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: `Error creating payment intent: ${(error as Error).message}`, }); } }; export const whIntentCreatedHandler = async ({ pIntentId, paymentId, }: { pIntentId: Payments["intent_id"]; paymentId: PaymentsId; }) => { return await db .updateTable("payments") .set({ intent_id: pIntentId, paymentstatus: PaymentStatusEnum.processing }) .where("id", "=", paymentId) .returning("id") .execute(); }; export const whIntentSucceededHandler = async ({ pIntentId, paymentId, pm_id, }: { pIntentId: Payments["intent_id"]; paymentId: PaymentsId; pm_id: string; }) => { try { const paymentMethod = await stripeApi.paymentMethods.retrieve(pm_id); await db.transaction().execute(async (trx) => { const payment = await trx .updateTable("payments") .set({ paid_at: new Date(), paymentmethod: paymentMethod.type, paymentstatus: PaymentStatusEnum.success, }) .where("intent_id", "=", pIntentId) .where("payments.id", "=", paymentId) .returning(["servizio_id", "ordine_id"]) .executeTakeFirstOrThrow(); const ordine = await trx .selectFrom("ordini") .selectAll("ordini") .where("ordine_id", "=", payment.ordine_id) .executeTakeFirstOrThrow(); await trx .updateTable("ordini") .where("ordine_id", "=", payment.ordine_id) .set({ isActive: true, }) .execute(); const servizio = await trx .selectFrom("servizio") .select("servizio.tipologia") .where("servizio_id", "=", payment.servizio_id) .executeTakeFirstOrThrow(); const utente = await getUser({ db: trx, userId: ordine.userid, }); switch (ordine.type) { case OrderTypeEnum.Acconto: await trx .updateTable("servizio") .where("servizio_id", "=", payment.servizio_id) .set({ decorrenza: new Date(), isOkAcconto: true, }) .execute(); await NewMail({ template: { mailType: "generic", props: { link: { href: `${env.BASE_URL}/area-riservata/payment-recap/${ordine.ordine_id}`, label: "Vai alla ricevuta", }, noreply: true, testo: `Il pagamento dell'acconto per il servizio ${ordine.packid} è stato confermato.`, title: "Pagamento Confermato ", }, }, mail: { subject: "Pagamento Confermato - Acconto Infoalloggi.it", to: utente.email, }, userId: utente.id, }); break; case OrderTypeEnum.Saldo: await trx .updateTable("servizio") .where("servizio_id", "=", payment.servizio_id) .set({ isOkConsulenza: servizio.tipologia === TipologiaPosizioneEnum.Transitorio, isOkSaldo: true, }) .execute(); await NewMail({ template: { mailType: "generic", props: { link: { href: `${env.BASE_URL}/area-riservata/payment-recap/${ordine.ordine_id}`, label: "Vai alla ricevuta", }, noreply: true, testo: `Il pagamento del saldo per il servizio ${ordine.packid} è stato confermato.`, title: "Pagamento Confermato ", }, }, mail: { subject: "Pagamento Confermato - Saldo Infoalloggi.it", to: utente.email, }, userId: utente.id, }); break; case OrderTypeEnum.Consulenza: await trx .updateTable("servizio") .where("servizio_id", "=", payment.servizio_id) .set({ isOkConsulenza: true, }) .execute(); await NewMail({ template: { mailType: "generic", props: { link: { href: `${env.BASE_URL}/area-riservata/payment-recap/${ordine.ordine_id}`, label: "Vai alla ricevuta", }, noreply: true, testo: `Il pagamento per la consulenza è stato confermato.`, title: "Pagamento Confermato ", }, }, mail: { subject: "Pagamento Confermato - Consulenza Infoalloggi.it", to: utente.email, }, userId: utente.id, }); break; case OrderTypeEnum.Altro: // TODO: Handle other types await NewMail({ template: { mailType: "pagamentoConferma", props: { receiptHref: `${env.BASE_URL}/area-riservata/payment-recap/${ordine.ordine_id}`, }, }, mail: { subject: "Pagamento Confermato", to: utente.email, }, userId: utente.id, }); break; } }); return true; } catch (error) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: `Error updating payment intent: ${(error as Error).message}`, }); } }; export const whIntentFailedHandler = async ({ pIntentId, }: { pIntentId: Payments["intent_id"]; }) => { const userId = await db .updateTable("payments") .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", to: utente.email, }, userId: utente.id, }); } return true; };