diff --git a/apps/web/lib/finalize-purchase.ts b/apps/web/lib/finalize-purchase.ts index b56ae5031..82a9d709f 100644 --- a/apps/web/lib/finalize-purchase.ts +++ b/apps/web/lib/finalize-purchase.ts @@ -1,8 +1,15 @@ import CourseModel, { Course } from "../models/Course"; import UserModel, { User } from "../models/User"; +import PurchaseModel, { Purchase } from "@models/Purchase"; +import DomainModel, { Domain } from "@models/Domain"; import { triggerSequences } from "./trigger-sequences"; import { recordActivity } from "./record-activity"; import { Constants, Progress } from "@courselit/common-models"; +import saleEmailTemplate from "@/templates/sale-email"; +import pug from "pug"; +import { send } from "@/services/mail"; +import { formattedLocaleDate } from "@ui-lib/utils"; +import { error } from "@/services/logger"; export default async ( userId: string, @@ -52,6 +59,56 @@ export default async ( purchaseId, }, }); + + await sendSaleNotificationToAdmins({ + user, + course, + purchaseId, + }); } } }; + +async function sendSaleNotificationToAdmins({ user, course, purchaseId }) { + try { + const domain: Domain | null = await DomainModel.findOne({ + _id: user?.domain, + }); + + const purchase: Purchase | null = await PurchaseModel.findOne({ + orderId: purchaseId, + }); + + const usersWithManagePermissions = await UserModel.find( + { + domain: domain?._id, + $or: [ + { userId: course.creatorId, permissions: "course:manage" }, + { permissions: "course:manage_any" }, + ], + }, + { email: 1 }, + ).lean(); + + const courseAdminsEmails = usersWithManagePermissions.map( + (x) => x.email, + ); + + const emailBody = pug.render(saleEmailTemplate, { + order: purchase?.orderId, + courseName: course.title, + coursePrice: course.cost, + date: formattedLocaleDate(purchase.purchasedOn), + email: user?.email, + hideCourseLitBranding: domain?.settings.hideCourseLitBranding, + }); + + await send({ + to: courseAdminsEmails, + subject: `Yay! You have made a sale!`, + body: emailBody, + }); + } catch (err) { + error("Failed to send sale notification mail", err); + } +} diff --git a/apps/web/pages/api/payment/initiate.ts b/apps/web/pages/api/payment/initiate.ts index 99ea26eff..3b6d3318e 100644 --- a/apps/web/pages/api/payment/initiate.ts +++ b/apps/web/pages/api/payment/initiate.ts @@ -6,7 +6,7 @@ import { getPaymentMethod } from "../../../payments"; import PurchaseModel from "../../../models/Purchase"; import finalizePurchase from "../../../lib/finalize-purchase"; import { error } from "../../../services/logger"; -import User from "@models/User"; +import UserModel from "@models/User"; import DomainModel, { Domain } from "@models/Domain"; import { auth } from "@/auth"; @@ -32,7 +32,7 @@ export default async function handler( let user; if (session) { - user = await User.findOne({ + user = await UserModel.findOne({ email: session.user!.email, domain: domain._id, active: true, diff --git a/apps/web/templates/sale-email.ts b/apps/web/templates/sale-email.ts new file mode 100644 index 000000000..9c8ad22ae --- /dev/null +++ b/apps/web/templates/sale-email.ts @@ -0,0 +1,56 @@ +const saleEmailTemplate = ` +doctype html +html + head + style(type='text/css'). + .cta-container { + margin: 32px 0px; + text-align: center; + } + .cta { + border: 1px solid #07077b; + border-radius: 4px; + padding: 4px 8px; + text-decoration: none; + color: white; + background-color: #07077b; + font-weight: bold; + } + .cta:hover { + background-color: #060665; + } + .courselit-branding-container { + margin: 40px 0px; + } + .courselit-branding-cta { + text-decoration: none; + color: #000000; + padding: 6px 10px; + background-color: #FFFFFF; + border: 1px solid; + border-radius: 6px; + text-align: center; + } + .signature { + padding-top: 20px; + } + body + p Yay! You have made a sale! + p Order: #{order} + p Date: #{date} + p Email: #{email} + p Course Title: #{courseName} + p Course Price: #{coursePrice} + p(class="signature") + | Best, + p #[a(href="https://x.com/courselit") @CourseLit] + if !hideCourseLitBranding + div(class="courselit-branding-container") + a( + href="https://courselit.app" + target="_blank" + class="courselit-branding-cta" + ) Powered by CourseLit +`; + +export default saleEmailTemplate;