auth-app-api / src /utils /email.js
AndreaNotes1's picture
Upload 46 files
4c6b1e8 verified
Raw History Blame Contribute Delete
7.23 kB
const nodemailer = require("nodemailer");
const { env } = require("../config/env");
const logger = require("./logger");
let transporter = null;
function getTransporter() {
if (transporter) return transporter;
transporter = nodemailer.createTransport({
host: env.smtp.host,
port: env.smtp.port,
secure: env.smtp.secure, // true for 465, false for other ports (STARTTLS)
auth: env.smtp.user ? { user: env.smtp.user, pass: env.smtp.pass } : undefined,
requireTLS: !env.smtp.secure, // enforce STARTTLS when not already on a TLS port
});
return transporter;
}
async function sendMail({ to, subject, html, text }) {
try {
const info = await getTransporter().sendMail({
from: env.emailFrom,
to,
subject,
html,
text,
});
return info;
} catch (err) {
// Email failures must never crash a request or leak SMTP credentials.
logger.error("Failed to send email", { subject, error: err.message });
return null;
}
}
function baseLayout(bodyHtml) {
return `
<div style="font-family:Arial,Helvetica,sans-serif;max-width:600px;margin:0 auto;color:#1a1a1a;">
<div style="padding:24px 0;border-bottom:2px solid #eee;">
<h2 style="margin:0;color:#111;">Aynwerd Academy</h2>
</div>
<div style="padding:24px 0;">${bodyHtml}</div>
<div style="padding:16px 0;border-top:1px solid #eee;font-size:12px;color:#777;">
This is an automated message regarding your order. Never share your password or
full payment reference by email. If you didn't make this request, contact
support immediately.
</div>
</div>`;
}
async function sendOrderPlacedEmail({ to, name, order, courseTitle, bankDetails, payoneerDetails }) {
const methodBlock =
order.paymentMethod === "bank_wire"
? `<h3>Bank wire transfer instructions</h3>
<table style="border-collapse:collapse;width:100%;font-size:14px;">
<tr><td style="padding:4px 0;color:#555;">Bank name</td><td><strong>${bankDetails.name}</strong></td></tr>
<tr><td style="padding:4px 0;color:#555;">Account name</td><td><strong>${bankDetails.accountName}</strong></td></tr>
<tr><td style="padding:4px 0;color:#555;">Account number</td><td><strong>${bankDetails.accountNumber}</strong></td></tr>
<tr><td style="padding:4px 0;color:#555;">Routing number</td><td><strong>${bankDetails.routingNumber}</strong></td></tr>
<tr><td style="padding:4px 0;color:#555;">SWIFT/BIC</td><td><strong>${bankDetails.swiftBic}</strong></td></tr>
<tr><td style="padding:4px 0;color:#555;">Bank address</td><td><strong>${bankDetails.address}</strong></td></tr>
</table>
<p>Please use order number <strong>${order.orderNumber}</strong> as your payment reference.</p>`
: `<h3>Payoneer payment instructions</h3>
<p>Send payment to Payoneer account <strong>${payoneerDetails.accountEmail}</strong>
or use your Payoneer app's "Request a Payment" link: <a href="${payoneerDetails.paymentUrl}">${payoneerDetails.paymentUrl}</a>.</p>
<p>Please use order number <strong>${order.orderNumber}</strong> as your payment note.</p>`;
const html = baseLayout(`
<p>Hi ${name},</p>
<p>Thanks for enrolling in <strong>${courseTitle}</strong>. Your order has been created and is
awaiting payment${order.status === "awaiting_confirmation" ? " confirmation" : ""}.</p>
<p><strong>Order number:</strong> ${order.orderNumber}<br/>
<strong>Amount due:</strong> ${order.currency} ${order.amount.toFixed(2)}<br/>
<strong>Payment method:</strong> ${order.paymentMethod === "bank_wire" ? "Bank wire transfer" : "Payoneer"}</p>
${methodBlock}
<p>Once our team confirms your payment, you'll receive a separate email and full course access
will be unlocked automatically on your account.</p>
`);
return sendMail({
to,
subject: `Your Aynwerd order ${order.orderNumber} β€” payment instructions`,
html,
text: `Order ${order.orderNumber} created for ${courseTitle}. Amount due: ${order.currency} ${order.amount.toFixed(2)}.`,
});
}
async function sendPaymentConfirmedEmail({ to, name, order, courseTitle }) {
const html = baseLayout(`
<p>Hi ${name},</p>
<p>Great news β€” we've confirmed your payment for <strong>${courseTitle}</strong>.</p>
<p><strong>Order number:</strong> ${order.orderNumber}<br/>
<strong>Amount paid:</strong> ${order.currency} ${order.amount.toFixed(2)}<br/>
<strong>Confirmed on:</strong> ${new Date(order.confirmedAt).toUTCString()}</p>
<p>Full access to the course has been unlocked on your account. Log in any time to get started.</p>
`);
return sendMail({
to,
subject: `Payment confirmed β€” order ${order.orderNumber}`,
html,
text: `Payment confirmed for order ${order.orderNumber}. Course access unlocked.`,
});
}
async function sendVoucherRedeemedEmail({ to, name, order, courseTitle, voucherCode }) {
const html = baseLayout(`
<p>Hi ${name},</p>
<p>Your voucher <strong>${voucherCode}</strong> has been redeemed for <strong>${courseTitle}</strong> β€”
no payment was required.</p>
<p><strong>Order number:</strong> ${order.orderNumber}</p>
<p>Full access to the course has been unlocked on your account. Log in any time to get started.</p>
`);
return sendMail({
to,
subject: `Voucher redeemed β€” ${courseTitle} unlocked`,
html,
text: `Voucher ${voucherCode} redeemed for ${courseTitle}. Course access unlocked. Order ${order.orderNumber}.`,
});
}
async function sendPaymentRejectedEmail({ to, name, order, courseTitle, reason }) {
const html = baseLayout(`
<p>Hi ${name},</p>
<p>We were unable to confirm your payment for <strong>${courseTitle}</strong> (order
<strong>${order.orderNumber}</strong>).</p>
${reason ? `<p><strong>Reason:</strong> ${reason}</p>` : ""}
<p>Please reply to this email or contact support so we can help resolve this.</p>
`);
return sendMail({
to,
subject: `Action needed on order ${order.orderNumber}`,
html,
text: `We could not confirm payment for order ${order.orderNumber}.${reason ? ` Reason: ${reason}` : ""}`,
});
}
async function sendAdminNewOrderAlert({ order, courseTitle, customerEmail }) {
if (!env.adminNotifyEmail) return null;
const html = baseLayout(`
<p>New order awaiting confirmation.</p>
<p><strong>Order:</strong> ${order.orderNumber}<br/>
<strong>Customer:</strong> ${customerEmail}<br/>
<strong>Course:</strong> ${courseTitle}<br/>
<strong>Amount:</strong> ${order.currency} ${order.amount.toFixed(2)}<br/>
<strong>Method:</strong> ${order.paymentMethod}</p>
<p>Review and confirm it from the admin dashboard.</p>
`);
return sendMail({
to: env.adminNotifyEmail,
subject: `New order ${order.orderNumber} awaiting confirmation`,
html,
});
}
module.exports = {
sendOrderPlacedEmail,
sendPaymentConfirmedEmail,
sendVoucherRedeemedEmail,
sendPaymentRejectedEmail,
sendAdminNewOrderAlert,
};