From 606fd55a420ee3bbd41a0d40f9c38208337783ef Mon Sep 17 00:00:00 2001 From: k4rli Date: Mon, 4 Aug 2025 11:53:27 +0300 Subject: [PATCH] feat(MED-131): use constants --- .../montonio-callback/[montonioId]/route.ts | 36 ++++++++++++++----- .../cart/montonio-callback/route.ts | 1 + 2 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 app/home/(user)/(dashboard)/cart/montonio-callback/route.ts diff --git a/app/home/(user)/(dashboard)/cart/montonio-callback/[montonioId]/route.ts b/app/home/(user)/(dashboard)/cart/montonio-callback/[montonioId]/route.ts index 0769f7c..15b6348 100644 --- a/app/home/(user)/(dashboard)/cart/montonio-callback/[montonioId]/route.ts +++ b/app/home/(user)/(dashboard)/cart/montonio-callback/[montonioId]/route.ts @@ -3,12 +3,21 @@ import { z } from "zod"; import { MontonioOrderToken } from "@/app/home/(user)/_components/cart/types"; import { loadCurrentUserAccount } from "@/app/home/(user)/_lib/server/load-user-account"; import { listProductTypes } from "@lib/data/products"; -import { placeOrder } from "@lib/data/cart"; +import { placeOrder, retrieveCart } from "@lib/data/cart"; import { createI18nServerInstance } from "~/lib/i18n/i18n.server"; +import { createOrder } from '~/lib/services/order.service'; const emailSender = process.env.EMAIL_SENDER; const siteUrl = process.env.NEXT_PUBLIC_SITE_URL!; +const ANALYSIS_PACKAGES_TYPE_HANDLE = 'analysis-packages'; +const MONTONIO_PAID_STATUS = 'PAID'; + +/** + * This is needed locally, because Montonio doesn't accept "localhost" redirect/notification URLs + */ +const LOCAL_MONTONIO_REDIRECT_FAKE_ORIGIN = 'webhook.site'; + const env = z .object({ emailSender: z @@ -62,21 +71,29 @@ const handleOrderToken = async (orderToken: string) => { const decoded = jwt.verify(orderToken, secretKey, { algorithms: ['HS256'], }) as MontonioOrderToken; - if (decoded.paymentStatus !== 'PAID') { + if (decoded.paymentStatus !== MONTONIO_PAID_STATUS) { return null; } try { - const [, , cartId] = decoded.merchantReferenceDisplay.split(':'); + const [,, cartId] = decoded.merchantReferenceDisplay.split(':'); if (!cartId) { throw new Error("Cart ID not found"); } + + const cart = await retrieveCart(cartId); + if (!cart) { + throw new Error("Cart not found"); + } + + const medusaOrder = await placeOrder(cartId, { revalidateCacheTags: true }); + await createOrder({ medusaOrder: medusaOrder }); + const { productTypes } = await listProductTypes(); - const analysisPackagesType = productTypes.find(({ metadata }) => metadata?.handle === 'analysis-packages'); - const order = await placeOrder(cartId, { revalidateCacheTags: true }); - const analysisPackageOrderItem = order.items?.find(({ product_type_id }) => product_type_id === analysisPackagesType?.id); + const analysisPackagesType = productTypes.find(({ metadata }) => metadata?.handle === ANALYSIS_PACKAGES_TYPE_HANDLE); + const analysisPackageOrderItem = medusaOrder.items?.find(({ product_type_id }) => product_type_id === analysisPackagesType?.id); return { - email: order.email, + email: medusaOrder.email, partnerLocationName: analysisPackageOrderItem?.metadata?.partner_location_name as string ?? '', analysisPackageName: analysisPackageOrderItem?.title ?? '', }; @@ -87,13 +104,13 @@ const handleOrderToken = async (orderToken: string) => { export async function GET(request: Request) { const { language } = await createI18nServerInstance(); - const baseUrl = new URL(env.siteUrl.replace("localhost", "webhook.site")); + const baseUrl = new URL(env.siteUrl.replace("localhost", LOCAL_MONTONIO_REDIRECT_FAKE_ORIGIN)); try { const orderToken = new URL(request.url).searchParams.get('order-token'); if (!orderToken) { throw new Error("Order token is missing"); } - + const account = await loadCurrentUserAccount(); if (!account) { throw new Error("Account not found in context"); @@ -109,6 +126,7 @@ export async function GET(request: Request) { if (email && analysisPackageName) { await sendEmail({ email, analysisPackageName, personName, partnerLocationName, language }); } else { + // @TODO send email for separate analyses console.error("Missing email or analysisPackageName", orderResult); } return Response.redirect(new URL('/home/order', baseUrl)) diff --git a/app/home/(user)/(dashboard)/cart/montonio-callback/route.ts b/app/home/(user)/(dashboard)/cart/montonio-callback/route.ts new file mode 100644 index 0000000..907bc74 --- /dev/null +++ b/app/home/(user)/(dashboard)/cart/montonio-callback/route.ts @@ -0,0 +1 @@ +export { GET } from "./[montonioId]/route";